pax_global_header 0000666 0000000 0000000 00000000064 15222637367 0014527 g ustar 00root root 0000000 0000000 52 comment=f25f9904e095c49b5aa6292c751ccda06b1c6ed0
Bluetooth-Devices-xiaomi-ble-741c848/ 0000775 0000000 0000000 00000000000 15222637367 0017364 5 ustar 00root root 0000000 0000000 Bluetooth-Devices-xiaomi-ble-741c848/.all-contributorsrc 0000664 0000000 0000000 00000000461 15222637367 0023216 0 ustar 00root root 0000000 0000000 {
"projectName": "xiaomi-ble",
"projectOwner": "bluetooth-devices",
"repoType": "github",
"repoHost": "https://github.com",
"files": [
"README.md"
],
"imageSize": 80,
"commit": true,
"commitConvention": "angular",
"contributors": [],
"contributorsPerLine": 7,
"skipCi": true
}
Bluetooth-Devices-xiaomi-ble-741c848/.editorconfig 0000664 0000000 0000000 00000000444 15222637367 0022043 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-Devices-xiaomi-ble-741c848/.flake8 0000664 0000000 0000000 00000000055 15222637367 0020537 0 ustar 00root root 0000000 0000000 [flake8]
exclude = docs
max-line-length = 88
Bluetooth-Devices-xiaomi-ble-741c848/.github/ 0000775 0000000 0000000 00000000000 15222637367 0020724 5 ustar 00root root 0000000 0000000 Bluetooth-Devices-xiaomi-ble-741c848/.github/ISSUE_TEMPLATE/ 0000775 0000000 0000000 00000000000 15222637367 0023107 5 ustar 00root root 0000000 0000000 Bluetooth-Devices-xiaomi-ble-741c848/.github/ISSUE_TEMPLATE/1-bug_report.md 0000664 0000000 0000000 00000000422 15222637367 0025735 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-Devices-xiaomi-ble-741c848/.github/ISSUE_TEMPLATE/2-feature-request.md 0000664 0000000 0000000 00000000672 15222637367 0026716 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-Devices-xiaomi-ble-741c848/.github/dependabot.yml 0000664 0000000 0000000 00000001351 15222637367 0023554 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/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
commit-message:
prefix: "chore(deps-ci): "
groups:
github-actions:
patterns:
- "*"
- package-ecosystem: "pip" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
Bluetooth-Devices-xiaomi-ble-741c848/.github/labels.toml 0000664 0000000 0000000 00000003515 15222637367 0023067 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-Devices-xiaomi-ble-741c848/.github/workflows/ 0000775 0000000 0000000 00000000000 15222637367 0022761 5 ustar 00root root 0000000 0000000 Bluetooth-Devices-xiaomi-ble-741c848/.github/workflows/ci.yml 0000664 0000000 0000000 00000004727 15222637367 0024111 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@v7
- uses: actions/setup-python@v6
with:
python-version: "3.10"
- uses: pre-commit/action@v3.0.1
# 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@v7
with:
fetch-depth: 0
- uses: wagoid/commitlint-github-action@v6
test:
strategy:
fail-fast: false
matrix:
python-version:
- "3.10"
- "3.11"
- "3.12"
- "3.13"
os:
- ubuntu-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v6
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@v7
with:
token: ${{ secrets.CODECOV_TOKEN }}
release:
runs-on: ubuntu-latest
environment: release
if: github.ref == 'refs/heads/main'
needs:
- test
- lint
- commitlint
permissions:
id-token: write
contents: write
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
# Run semantic release:
# - Update CHANGELOG.md
# - Update version in code
# - Create git tag
# - Create GitHub release
- name: Python Semantic Release
id: release
uses: python-semantic-release/python-semantic-release@v10.5.3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Upload package to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
if: steps.release.outputs.released == 'true'
- name: Upload Github Release Assets
uses: python-semantic-release/publish-action@v10.5.3
if: steps.release.outputs.released == 'true'
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ steps.release.outputs.tag }}
Bluetooth-Devices-xiaomi-ble-741c848/.github/workflows/hacktoberfest.yml 0000664 0000000 0000000 00000000534 15222637367 0026332 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.6.0
with:
github_token: ${{ secrets.GH_PAT }}
Bluetooth-Devices-xiaomi-ble-741c848/.github/workflows/issue-manager.yml 0000664 0000000 0000000 00000001340 15222637367 0026242 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.8.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-Devices-xiaomi-ble-741c848/.github/workflows/labels.yml 0000664 0000000 0000000 00000000775 15222637367 0024757 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@v7
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: 3.12
- 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-Devices-xiaomi-ble-741c848/.gitignore 0000664 0000000 0000000 00000004066 15222637367 0021362 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-Devices-xiaomi-ble-741c848/.gitpod.yml 0000664 0000000 0000000 00000000306 15222637367 0021452 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-Devices-xiaomi-ble-741c848/.idea/ 0000775 0000000 0000000 00000000000 15222637367 0020344 5 ustar 00root root 0000000 0000000 Bluetooth-Devices-xiaomi-ble-741c848/.idea/govee-ble.iml 0000664 0000000 0000000 00000000515 15222637367 0022715 0 ustar 00root root 0000000 0000000
Bluetooth-Devices-xiaomi-ble-741c848/.idea/watcherTasks.xml 0000664 0000000 0000000 00000005253 15222637367 0023536 0 ustar 00root root 0000000 0000000
Bluetooth-Devices-xiaomi-ble-741c848/.idea/workspace.xml 0000664 0000000 0000000 00000006731 15222637367 0023073 0 ustar 00root root 0000000 0000000
1720200172152
1720200172152
Bluetooth-Devices-xiaomi-ble-741c848/.pre-commit-config.yaml 0000664 0000000 0000000 00000003061 15222637367 0023645 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: [pre-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: v4.16.4
hooks:
- id: commitizen
stages: [commit-msg]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.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: v3.1.0
# hooks:
# - id: prettier
- repo: https://github.com/asottile/pyupgrade
rev: v3.21.2
hooks:
- id: pyupgrade
- repo: https://github.com/PyCQA/isort
rev: 9.0.0a3
hooks:
- id: isort
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 26.5.1
hooks:
- id: black
- repo: https://github.com/codespell-project/codespell
rev: v2.4.2
hooks:
- id: codespell
- repo: https://github.com/PyCQA/flake8
rev: 7.3.0
hooks:
- id: flake8
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v2.1.0
hooks:
- id: mypy
additional_dependencies: []
Bluetooth-Devices-xiaomi-ble-741c848/.readthedocs.yml 0000664 0000000 0000000 00000001004 15222637367 0022445 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-Devices-xiaomi-ble-741c848/CHANGELOG.md 0000664 0000000 0000000 00000163417 15222637367 0021211 0 ustar 00root root 0000000 0000000 # CHANGELOG
## v0.39.0 (2025-05-05)
### Features
- Add xiaomi mbs17 ([#171](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/171),
[`4468b52`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/4468b52b6ef1c849af9b9f0f2c512a23315462d9))
Co-authored-by: Ernst
## v0.38.1 (2025-05-05)
### Bug Fixes
- Add hs1bb_mi to sleepy devices list
([`aa944d9`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/aa944d92e86b39ce79d0c291a61b1e5bb650ac8a))
### Chores
- **deps**: Bump aiohttp from 3.11.16 to 3.11.17
([#166](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/166),
[`39dc292`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/39dc2929b74911b5e3b0c953a055581c7bb2ef01))
- **deps**: Bump aiohttp from 3.11.17 to 3.11.18
([#169](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/169),
[`c5bfd2b`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/c5bfd2b8d57703642ba4dd89031cad7e1fdecc67))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps**: Bump orjson from 3.10.16 to 3.10.18
([#170](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/170),
[`2fa41a0`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/2fa41a07b035941962d6a3b1526b4ef8ed50e629))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
## v0.38.0 (2025-04-15)
### Chores
- **pre-commit.ci**: Pre-commit autoupdate
([#165](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/165),
[`1d958af`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/1d958af6e974c619f031d0216c313bc88539da9c))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
### Features
- Add support for XMWS01XS ([#161](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/161),
[`2401e72`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/2401e720844f22a129e0a52ebeca08a366c8a3d7))
## v0.37.0 (2025-04-11)
### Chores
- **deps**: Bump aiohttp from 3.11.14 to 3.11.16
([#160](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/160),
[`12093dc`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/12093dc3d76f3ef2cfc14adb0ab9da90201dd598))
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps-dev**: Bump pytest-cov from 6.0.0 to 6.1.1
([#159](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/159),
[`446907c`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/446907cffa4bbd668246bd545e8a4882128ca5ee))
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#157](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/157),
[`8831729`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/8831729d0ebf0d41a74c83933960f02970b85eb0))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#162](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/162),
[`35fa4d7`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/35fa4d7d5722cb68966b79fc6961eb37f149841f))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
### Features
- Support Body Composition Scale S400 (MJTZC01YM)
([#163](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/163),
[`5840470`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/5840470b93519946771afafe7bcffdd3ca855f76))
Signed-off-by: zry98
## v0.36.0 (2025-03-31)
### Features
- Add ks1 and ks1bp ([#156](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/156),
[`415a3b3`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/415a3b38b113256e700284198d9a2d911886d79a))
## v0.35.0 (2025-03-29)
### Chores
- Update dependabot.yml to include GHA
([`67fe52b`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/67fe52b8ded47f0c2bfbfe5fe14bb4ecfae3dfac))
- **deps**: Bump orjson from 3.10.15 to 3.10.16
([#154](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/154),
[`b90653c`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/b90653c96b35c2fa573c6207ec0f868f1f65fb2b))
Bumps [orjson](https://github.com/ijl/orjson) from 3.10.15 to 3.10.16. - [Release
notes](https://github.com/ijl/orjson/releases) -
[Changelog](https://github.com/ijl/orjson/blob/master/CHANGELOG.md) -
[Commits](https://github.com/ijl/orjson/compare/3.10.15...3.10.16)
--- updated-dependencies: - dependency-name: orjson dependency-type: direct:production
update-type: version-update:semver-patch ...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps-ci**: Bump the github-actions group with 8 updates
([#152](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/152),
[`e1d1f5c`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/e1d1f5c4f00c33ca7f680cf132b9ff0a16ba4f03))
* chore(deps-ci): bump the github-actions group with 8 updates
Bumps the github-actions group with 8 updates:
| Package | From | To | | --- | --- | --- | |
[actions/checkout](https://github.com/actions/checkout) | `3` | `4` | |
[actions/setup-python](https://github.com/actions/setup-python) | `3` | `5` | |
[pre-commit/action](https://github.com/pre-commit/action) | `3.0.0` | `3.0.1` | |
[wagoid/commitlint-github-action](https://github.com/wagoid/commitlint-github-action) | `5` | `6`
| | [codecov/codecov-action](https://github.com/codecov/codecov-action) | `3` | `5` | |
[relekang/python-semantic-release](https://github.com/relekang/python-semantic-release) | `7.34.6`
| `9.21.0` | |
[browniebroke/hacktoberfest-labeler-action](https://github.com/browniebroke/hacktoberfest-labeler-action)
| `2.2.0` | `2.3.0` | | [tiangolo/issue-manager](https://github.com/tiangolo/issue-manager) |
`0.4.0` | `0.5.1` |
Updates `actions/checkout` from 3 to 4 - [Release
notes](https://github.com/actions/checkout/releases) -
[Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) -
[Commits](https://github.com/actions/checkout/compare/v3...v4)
Updates `actions/setup-python` from 3 to 5 - [Release
notes](https://github.com/actions/setup-python/releases) -
[Commits](https://github.com/actions/setup-python/compare/v3...v5)
Updates `pre-commit/action` from 3.0.0 to 3.0.1 - [Release
notes](https://github.com/pre-commit/action/releases) -
[Commits](https://github.com/pre-commit/action/compare/v3.0.0...v3.0.1)
Updates `wagoid/commitlint-github-action` from 5 to 6 -
[Changelog](https://github.com/wagoid/commitlint-github-action/blob/master/CHANGELOG.md) -
[Commits](https://github.com/wagoid/commitlint-github-action/compare/v5...v6)
Updates `codecov/codecov-action` from 3 to 5 - [Release
notes](https://github.com/codecov/codecov-action/releases) -
[Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) -
[Commits](https://github.com/codecov/codecov-action/compare/v3...v5)
Updates `relekang/python-semantic-release` from 7.34.6 to 9.21.0 - [Release
notes](https://github.com/relekang/python-semantic-release/releases) -
[Changelog](https://github.com/python-semantic-release/python-semantic-release/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/relekang/python-semantic-release/compare/v7.34.6...v9.21.0)
Updates `browniebroke/hacktoberfest-labeler-action` from 2.2.0 to 2.3.0 - [Release
notes](https://github.com/browniebroke/hacktoberfest-labeler-action/releases) -
[Changelog](https://github.com/browniebroke/hacktoberfest-labeler-action/blob/main/CHANGELOG.md) -
[Commits](https://github.com/browniebroke/hacktoberfest-labeler-action/compare/v2.2.0...v2.3.0)
Updates `tiangolo/issue-manager` from 0.4.0 to 0.5.1 - [Release
notes](https://github.com/tiangolo/issue-manager/releases) -
[Commits](https://github.com/tiangolo/issue-manager/compare/0.4.0...0.5.1)
--- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions
- dependency-name: actions/setup-python dependency-type: direct:production
- dependency-name: pre-commit/action dependency-type: direct:production
update-type: version-update:semver-patch
- dependency-name: wagoid/commitlint-github-action dependency-type: direct:production
- dependency-name: codecov/codecov-action dependency-type: direct:production
- dependency-name: relekang/python-semantic-release dependency-type: direct:production
- dependency-name: browniebroke/hacktoberfest-labeler-action dependency-type: direct:production
update-type: version-update:semver-minor
- dependency-name: tiangolo/issue-manager dependency-type: direct:production
dependency-group: github-actions ...
Signed-off-by: dependabot[bot]
* chore: update commitlint config
* chore: update pyproject for new psr
* chore: migrate workflow to upload python package
---------
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston
- **deps-dev**: Bump flake8 from 7.1.2 to 7.2.0
([#153](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/153),
[`7d91379`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/7d91379e515a51f49de12f54c05abcc65584d2d4))
Bumps [flake8](https://github.com/pycqa/flake8) from 7.1.2 to 7.2.0. -
[Commits](https://github.com/pycqa/flake8/compare/7.1.2...7.2.0)
--- updated-dependencies: - dependency-name: flake8 dependency-type: direct:development
update-type: version-update:semver-minor ...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
### Features
- Use trusted publishing for PyPI ([#155](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/155),
[`95a54ad`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/95a54ad52cff260b26f3b05cd865f028afc3905d))
## v0.34.1 (2025-03-29)
### Bug Fixes
- Rewrite struct ([#151](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/151),
[`98e313b`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/98e313be6b057407385893833f4d6d9482568515))
## v0.34.0 (2025-03-29)
### Chores
- Add Python 3.13 to the CI ([#115](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/115),
[`534a5e8`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/534a5e81448f6f52f76c754ce38a50582eb1c168))
- Create dependabot.yml
([`b291db2`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/b291db278bf3fedcf0f9b678a0eb1c95f5898c45))
- Update deps for Python 3.13 ([#123](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/123),
[`ee474e7`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/ee474e789df55f69f0eb77dfd82e6610ddf35461))
- **deps**: Bump aiohttp from 3.10.10 to 3.11.11
([#110](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/110),
[`a32edf3`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/a32edf330de1a0906f3df0f45b9672847e834b85))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps**: Bump aiohttp from 3.11.11 to 3.11.12
([#137](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/137),
[`ae27c17`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/ae27c1732682acd08d3a1f778e5b02a9a5960cab))
- **deps**: Bump aiohttp from 3.11.12 to 3.11.13
([#142](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/142),
[`a20032e`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/a20032e70f083bda4fb9d24536df4f402dfb2f38))
Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.11.12 to 3.11.13. - [Release
notes](https://github.com/aio-libs/aiohttp/releases) -
[Changelog](https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst) -
[Commits](https://github.com/aio-libs/aiohttp/compare/v3.11.12...v3.11.13)
--- updated-dependencies: - dependency-name: aiohttp dependency-type: direct:production
update-type: version-update:semver-patch ...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps**: Bump aiohttp from 3.11.13 to 3.11.14
([#144](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/144),
[`237e9fe`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/237e9fe04a73ee6678b41ccdf4424d52143755d1))
--- updated-dependencies: - dependency-name: aiohttp dependency-type: direct:production
update-type: version-update:semver-patch ...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps**: Bump bluetooth-sensor-state-data from 1.7.1 to 1.7.5
([#136](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/136),
[`8df0798`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/8df07985636c5650c8802ff837f5959704005ac8))
- **deps**: Bump certifi from 2023.11.17 to 2024.7.4
([#116](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/116),
[`32ece70`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/32ece7046f5a44b1476a7d00919d3a7a6b4f8bcb))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps**: Bump cryptography from 41.0.7 to 43.0.1
([#120](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/120),
[`b5c6524`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/b5c6524760731d953b2bc01fe4d1d626758656fa))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps**: Bump idna from 3.6 to 3.7
([#118](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/118),
[`ff0ccac`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/ff0ccac7c2921f09c2aaec7d6bc7aa8fdd4919b2))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps**: Bump jinja2 from 3.1.2 to 3.1.5
([#117](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/117),
[`76120db`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/76120db87270e1ddcc3fbb3f980f1b6ebee5f299))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps**: Bump jinja2 from 3.1.5 to 3.1.6
([#146](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/146),
[`8f64c7b`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/8f64c7b7b92c6e8d64d2f874f9bb03793ff46bd7))
Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.5 to 3.1.6. - [Release
notes](https://github.com/pallets/jinja/releases) -
[Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst) -
[Commits](https://github.com/pallets/jinja/compare/3.1.5...3.1.6)
--- updated-dependencies: - dependency-name: jinja2 dependency-type: indirect ...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps**: Bump myst-parser from 0.18.1 to 1.0.0
([#126](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/126),
[`f9f1e0b`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/f9f1e0bfb9a764c56cd4527d74f49f6bd66d05c6))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps**: Bump myst-parser from 1.0.0 to 3.0.1
([#132](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/132),
[`68e4f90`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/68e4f90283e1806e65600234d4730f49e166cb5c))
Bumps [myst-parser](https://github.com/executablebooks/MyST-Parser) from 1.0.0 to 3.0.1. - [Release
notes](https://github.com/executablebooks/MyST-Parser/releases) -
[Changelog](https://github.com/executablebooks/MyST-Parser/blob/master/CHANGELOG.md) -
[Commits](https://github.com/executablebooks/MyST-Parser/compare/v1.0.0...v3.0.1)
--- updated-dependencies: - dependency-name: myst-parser dependency-type: direct:production
update-type: version-update:semver-major ...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps**: Bump pycryptodomex from 3.21.0 to 3.22.0
([#145](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/145),
[`492010a`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/492010a3606a1e6426dd7d4e3a22360214ac75ca))
- **deps**: Bump requests from 2.31.0 to 2.32.2
([#121](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/121),
[`b1355f2`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/b1355f262cb134aecb794552d82206bab0bf7c9b))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps**: Bump sphinx from 5.3.0 to 6.2.1
([#127](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/127),
[`a2da09e`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/a2da09e57f8add15f4b2939d3df9b4bf88821f61))
- **deps**: Bump sphinx from 6.2.1 to 7.4.7
([#134](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/134),
[`6a5e11c`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/6a5e11cd5a0140542c2a2f02cfaaeb60e2b60a43))
- **deps**: Bump sphinx-rtd-theme from 1.3.0 to 2.0.0
([#125](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/125),
[`f3133ed`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/f3133ed85471250f4e5f9ffc9bd72d6f591fe058))
Bumps [sphinx-rtd-theme](https://github.com/readthedocs/sphinx_rtd_theme) from 1.3.0 to 2.0.0. -
[Changelog](https://github.com/readthedocs/sphinx_rtd_theme/blob/master/docs/changelog.rst) -
[Commits](https://github.com/readthedocs/sphinx_rtd_theme/compare/1.3.0...2.0.0)
--- updated-dependencies: - dependency-name: sphinx-rtd-theme dependency-type: direct:production
update-type: version-update:semver-major ...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps**: Bump sphinx-rtd-theme from 2.0.0 to 3.0.2
([#131](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/131),
[`bba51cd`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/bba51cd0a262e520ae9f8a3b15c3517aa9f047f9))
- **deps**: Bump urllib3 from 2.1.0 to 2.2.2
([#119](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/119),
[`372fe44`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/372fe44d7164fbba9231353def30865679e337da))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps-dev**: Bump black from 23.12.1 to 24.10.0
([#114](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/114),
[`3f74088`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/3f74088640367cfc79a2c334793cba1a1754e44a))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps-dev**: Bump black from 24.10.0 to 25.1.0
([#129](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/129),
[`00df853`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/00df853e955ee6cfaf7397d0924ed96829b09d50))
- **deps-dev**: Bump flake8 from 7.0.0 to 7.1.1
([#111](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/111),
[`ee96510`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/ee96510187c70b698c5def2c21aa86012a141879))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps-dev**: Bump flake8 from 7.1.1 to 7.1.2
([#139](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/139),
[`40baa29`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/40baa299a47e744f0a465d7a6f21eebec8196bfb))
Bumps [flake8](https://github.com/pycqa/flake8) from 7.1.1 to 7.1.2. -
[Commits](https://github.com/pycqa/flake8/compare/7.1.1...7.1.2)
--- updated-dependencies: - dependency-name: flake8 dependency-type: direct:development
update-type: version-update:semver-patch ...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps-dev**: Bump isort from 5.13.2 to 6.0.0
([#130](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/130),
[`7f9cb1c`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/7f9cb1c0cb408ffbf832ec20ab3a430f0f93b91c))
Bumps [isort](https://github.com/pycqa/isort) from 5.13.2 to 6.0.0. - [Release
notes](https://github.com/pycqa/isort/releases) -
[Changelog](https://github.com/PyCQA/isort/blob/main/CHANGELOG.md) -
[Commits](https://github.com/pycqa/isort/compare/5.13.2...6.0.0)
--- updated-dependencies: - dependency-name: isort dependency-type: direct:development
update-type: version-update:semver-major ...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps-dev**: Bump isort from 6.0.0 to 6.0.1
([#143](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/143),
[`e57a9e6`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/e57a9e6f0e8333bf88620c0ea405088b81963486))
Bumps [isort](https://github.com/PyCQA/isort) from 6.0.0 to 6.0.1. - [Release
notes](https://github.com/PyCQA/isort/releases) -
[Changelog](https://github.com/PyCQA/isort/blob/main/CHANGELOG.md) -
[Commits](https://github.com/PyCQA/isort/compare/6.0.0...6.0.1)
--- updated-dependencies: - dependency-name: isort dependency-type: direct:development
update-type: version-update:semver-patch ...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps-dev**: Bump mypy from 1.14.1 to 1.15.0
([#135](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/135),
[`1ac5955`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/1ac5955acd59087ca10e7ad169ae3aa3ef8eaf25))
Bumps [mypy](https://github.com/python/mypy) from 1.14.1 to 1.15.0. -
[Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) -
[Commits](https://github.com/python/mypy/compare/v1.14.1...v1.15.0)
--- updated-dependencies: - dependency-name: mypy dependency-type: direct:development
update-type: version-update:semver-minor ...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps-dev**: Bump mypy from 1.8.0 to 1.14.1
([#113](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/113),
[`f3cceb4`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/f3cceb42c506bb2142e4f74f8f6194061631028b))
- **deps-dev**: Bump pytest from 7.4.4 to 8.3.4
([#112](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/112),
[`9a4109c`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/9a4109c8ada297babd478781e80c23f8edc81907))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps-dev**: Bump pytest from 8.3.4 to 8.3.5
([#141](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/141),
[`7a1f267`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/7a1f267107c5255fd7e70a60d8e3b7deb4d17a8e))
Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.3.4 to 8.3.5. - [Release
notes](https://github.com/pytest-dev/pytest/releases) -
[Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) -
[Commits](https://github.com/pytest-dev/pytest/compare/8.3.4...8.3.5)
--- updated-dependencies: - dependency-name: pytest dependency-type: direct:development
update-type: version-update:semver-patch ...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps-dev**: Bump pytest-cov from 4.1.0 to 6.0.0
([#124](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/124),
[`1df7541`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/1df7541126830f03ca2de0886d25c1a2d1745b16))
Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 4.1.0 to 6.0.0. -
[Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst) -
[Commits](https://github.com/pytest-dev/pytest-cov/compare/v4.1.0...v6.0.0)
--- updated-dependencies: - dependency-name: pytest-cov dependency-type: direct:development
update-type: version-update:semver-major ...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **deps-dev**: Bump zipp from 3.17.0 to 3.19.1
([#122](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/122),
[`bafacf5`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/bafacf57b1aa4cddabd9c755e668943cfa65b3ba))
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#101](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/101),
[`7951a00`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/7951a002e3570d42be328d3d0f16a93048690ef0))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#102](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/102),
[`6599a90`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/6599a907cd470a606863d7bf9c456696d3298a9a))
updates: - [github.com/commitizen-tools/commitizen: v3.30.1 →
v3.31.0](https://github.com/commitizen-tools/commitizen/compare/v3.30.1...v3.31.0)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#103](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/103),
[`fb68075`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/fb68075c12fd2f652d83387df5496b63006e1d8b))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#104](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/104),
[`705bc17`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/705bc1722be9a74d5692f8a237e0c5bbb0e4821c))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#106](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/106),
[`c4bc481`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/c4bc481244624e20c1ba026891ee2482c4c4024e))
updates: - [github.com/asottile/pyupgrade: v3.19.0 →
v3.19.1](https://github.com/asottile/pyupgrade/compare/v3.19.0...v3.19.1) -
[github.com/pre-commit/mirrors-mypy: v1.13.0 →
v1.14.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.13.0...v1.14.0)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#109](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/109),
[`f12043e`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/f12043e357a25abda66460e2b12b1aeac70e2a92))
- **pre-commit.ci**: Pre-commit autoupdate
([#128](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/128),
[`28485cd`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/28485cdbac8415b1d00bed504f4865482b270960))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#133](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/133),
[`becc164`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/becc164c9cfd4cc0e332b05689fef869f693181c))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#138](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/138),
[`82c2fcb`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/82c2fcbc445eaf831a613d37fa1ebeb38dfdd4ac))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#140](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/140),
[`6170b12`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/6170b12726629107c43da324e056084df1dd7187))
updates: - [github.com/commitizen-tools/commitizen: v4.2.1 →
v4.4.1](https://github.com/commitizen-tools/commitizen/compare/v4.2.1...v4.4.1) -
[github.com/PyCQA/isort: 6.0.0 → 6.0.1](https://github.com/PyCQA/isort/compare/6.0.0...6.0.1)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#99](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/99),
[`6f9b5c2`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/6f9b5c2bf069083fd795bcfa7c601ae86abd1d3a))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
### Features
- Add support for MJWSD06MMC ([#150](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/150),
[`51cbe1c`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/51cbe1c364d6983decf9596fee8d1caf4501ff84))
## v0.33.0 (2024-10-21)
### Chores
- **pre-commit.ci**: Pre-commit autoupdate
([#97](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/97),
[`cf7a512`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/cf7a512c931326e6a64ae1e462e4e3ebed71c3b0))
updates: - [github.com/commitizen-tools/commitizen: v3.29.0 →
v3.29.1](https://github.com/commitizen-tools/commitizen/compare/v3.29.0...v3.29.1) -
[github.com/pre-commit/pre-commit-hooks: v4.6.0 →
v5.0.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.6.0...v5.0.0) -
[github.com/asottile/pyupgrade: v3.17.0 →
v3.18.0](https://github.com/asottile/pyupgrade/compare/v3.17.0...v3.18.0) -
[github.com/psf/black-pre-commit-mirror: 24.8.0 →
24.10.0](https://github.com/psf/black-pre-commit-mirror/compare/24.8.0...24.10.0) -
[github.com/PyCQA/bandit: 1.7.9 → 1.7.10](https://github.com/PyCQA/bandit/compare/1.7.9...1.7.10)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
### Features
- Add support for extracting cloud keys
([#98](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/98),
[`fdc3b90`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/fdc3b90cc07cd7e86e031c78bbb379800f549e72))
## v0.32.0 (2024-09-07)
### Features
- Support xiaomi motion sensor 2s - global version (XMPIR02SGXS)
([#95](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/95),
[`7e30569`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/7e30569a56b2668cd88ac1b4ee3d7454901e0c37))
It's actually the same as the China mainland version (XMPIR02SXS vs XMPIR02SGXS). The only change is
the device id as the code change indicates. The change is manually verified by Home Assistant (no
change except use the new xiaomi-ble package)
Hardware Info: - https://www.mi.com/global/product/xiaomi-motion-sensor-2s/
## v0.31.1 (2024-08-28)
### Bug Fixes
- Add xmosb01xs as a sleepy device ([#94](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/94),
[`648c7f8`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/648c7f80e4c1e9e9868b974e49e7da564457c094))
this battery device is really a sleepy device. it won't broadcast any occupancy message unless it is
changed.
## v0.31.0 (2024-08-27)
### Chores
- **pre-commit.ci**: Pre-commit autoupdate
([#85](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/85),
[`86c8e6b`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/86c8e6b0388c8afd3a6787587d5575f263eac4ab))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#86](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/86),
[`c9aa15c`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/c9aa15c7a41af6e83c1d6bed535e1a6b59f0a92e))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#87](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/87),
[`daa5786`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/daa5786e8488b2cdaf823eee5dd2236bdc6ce6fe))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#88](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/88),
[`8ec6e14`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/8ec6e14e0b7a27205d763fb7c5a8ca01218dc634))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- **pre-commit.ci**: Pre-commit autoupdate
([#89](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/89),
[`cf115f6`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/cf115f6b94ab461afab6cc2c4f6d4f64b310ccae))
updates: - [github.com/pre-commit/mirrors-mypy: v1.11.1 →
v1.11.2](https://github.com/pre-commit/mirrors-mypy/compare/v1.11.1...v1.11.2)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
### Features
- Support xiaomi occupancy(human presence) sensor (XMOSB01XS)
([#93](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/93),
[`f10c72b`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/f10c72b166572e8e711469da693192cdab5049c2))
feat: support 2024 new battery based xiaomi mijia occupancy(human presence) sensor (XMOSB01XS)
New test cases are added. Also the change is verified by Home Assistant (with a small additional
change in xiaomi-ble component for additional new properties)
Hardware Info: - https://www.aliexpress.com/i/1005007104780534.html -
https://www.mi.com/shop/buy/detail?product_id=19994
## v0.30.2 (2024-07-05)
### Bug Fixes
- Force release
([`cf7d48c`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/cf7d48c2194666eaede56ebd9ab3bfbc5ddfe3b1))
- Force release
([`7bcbda1`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/7bcbda1a6468fad98a0959cc05001ddbd478b81b))
- Force release
([`bae0ff3`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/bae0ff33e69ac4b40f5e6f090e3dbfd7af241ff8))
- Force release
([`2ed0584`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/2ed05848718d1d80a7edb410bd7a229f8334d4de))
### Chores
- **pre-commit.ci**: Pre-commit autoupdate
([#82](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/82),
[`0ee3610`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/0ee3610c1df7e24f32417c8ba5fd37d4a1700ced))
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
## v0.30.1 (2024-06-25)
### Bug Fixes
- Fix license classifier ([#81](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/81),
[`27072da`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/27072da63e01204f3bae81b7ddf734790896f0b5))
Looks good :) thanks
## v0.30.0 (2024-06-17)
### Features
- Extend battery level reading to gcls002
([`49530c5`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/49530c53893da290c26c4236d714a67df58612ff))
feat: extend battery level reading to gcls002
## v0.29.0 (2024-05-05)
### Features
- Add support for Linptech RS1BB(MI)
([#77](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/77),
[`57d070c`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/57d070c71282c43882bc64117c709cdf1aec8f09))
* feat: add support for RS1BB
## v0.28.0 (2024-03-24)
### Features
- Allow one failed decryption before reauth
([#76](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/76),
[`4682109`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/46821098f2d34b4c0c7c0244708500b4aa72f19e))
## v0.27.1 (2024-03-17)
### Bug Fixes
- Remove mac workaround ([#75](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/75),
[`c5a900c`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/c5a900c5d6e23b62769a16c0840ceed174c3a8da))
## v0.27.0 (2024-03-10)
### Features
- Add ptx and xmpiro2sx ([#74](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/74),
[`b0bb42f`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/b0bb42f777c9c5bdc6bc12590334bfc741b199a0))
* feat: add ptx and xmpiro2sx
* fix: rename ptx
## v0.26.2 (2024-03-10)
### Bug Fixes
- Handle jagged stairstep for LYWSD03MMC humidity
([#73](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/73),
[`efb3229`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/efb322909933ac820b32012913ff3c35f9ca3659))
* fix: handle jagged stairstep for LYWSD03MMC humidity
## v0.26.1 (2024-02-18)
### Bug Fixes
- Typo in lock events ([#71](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/71),
[`9918c70`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/9918c7004f4ed4d12045bcb486dc762064e8510f))
## v0.26.0 (2024-02-18)
### Features
- Add support for locks and fingerprint readers
([#70](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/70),
[`2cffc70`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/2cffc70d451614a671833274947911a123a8f3b0))
* feat: add support for locks and fingerprint readers
* fix: add key id type
* fix: typo in fingerprint event
* fix: add fingerprint event
* fix: typo in lock event
## v0.25.2 (2024-02-02)
### Bug Fixes
- Proper event_properties formatting
([#69](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/69),
[`86bced3`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/86bced38755551014d1273c69c6b80b79e3eb05a))
* fix: proper event_properties formatting
* fix: fix tests property_events
* fix: formatting of the tests
## v0.25.1 (2024-02-02)
### Bug Fixes
- Rename rubiks_cube to cube ([#68](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/68),
[`65c7b3d`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/65c7b3d830bf9256ea60b8d8a15236a49b6ddd65))
## v0.25.0 (2024-02-02)
### Features
- Add dimmer and rubiks cube events ([#67](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/67),
[`33db2b0`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/33db2b09d360a9e2f010ac2ad2ec5d199e65cf3f))
## v0.24.1 (2024-02-01)
### Bug Fixes
- Postfix plus and min ([#66](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/66),
[`d8caf82`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/d8caf825ea08bda7101c13f15d50db053c667e3c))
## v0.24.0 (2024-01-31)
### Features
- Add remote buttons ([#65](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/65),
[`380a803`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/380a80301e126150e87bb67e47c30c93dd28c0ff))
## v0.23.1 (2024-01-28)
### Bug Fixes
- Use postfix id for double buttons ([#64](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/64),
[`6469a6d`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/6469a6dc2319549d5ee915135738573436c959da))
## v0.23.0 (2024-01-27)
### Features
- Add button devices with multiple buttons
([#63](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/63),
[`740679c`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/740679ce362d4201c816655e997f8223021fa7a1))
## v0.22.0 (2024-01-27)
### Features
- Add events for single button devices
([#62](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/62),
[`2110db9`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/2110db95c2670bfeda24ed7c0d0bf87bf559d45e))
## v0.21.2 (2024-01-11)
### Bug Fixes
- Bump pycryptodomex ([#61](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/61),
[`8bcefae`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/8bcefaed9b8aa106cf37321793e147287779d4da))
* fix: bump pycryptodomex
* fix: update poetry lock file
* chore: run poetry update
---------
Co-authored-by: J. Nick Koston
### Chores
- Fix release
([`1d403f0`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/1d403f0b782fc1e0ba738cf9660576429286b7a5))
## v0.21.1 (2023-07-26)
### Bug Fixes
- Add unit of measurement to counter
([#56](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/56),
[`22956f8`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/22956f87269859e9ca94fb96c55a9a90fe45a4ac))
fix: add unit of measurment to counter
## v0.21.0 (2023-07-26)
### Features
- Add support for toothbrushes ([#55](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/55),
[`b3534f9`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/b3534f9daeef535372ffd5746d6662b1869f084e))
* feat: add support for toothbrushes
* feat: add score sensor for toothbrushes
* fix: precommit errors
## v0.20.0 (2023-07-23)
### Features
- Add mi scale v1 ([#54](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/54),
[`57dc01a`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/57dc01a0f665af945540f7d01356030ce065b96a))
* feat: add mi scale v1
* fix: check for mass not removed
* fix: check for mass removed before reporting final data
## v0.19.1 (2023-07-20)
### Bug Fixes
- Remove some dependencies ([#53](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/53),
[`9d11d62`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/9d11d620557ec95d0bdf9112b8cf69e82031a006))
* fix: remove some dependencies
* fix: poetry lock
## v0.19.0 (2023-07-20)
### Features
- Switch from pycryptodomex to cryptography for v4 and v5
([#48](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/48),
[`382beea`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/382beeadea23216c185428d2ada784a3e6fe6660))
* feat: switch from pycryptodomex to cryptography for v4 and v5
## v0.18.2 (2023-07-19)
### Bug Fixes
- Use only kg for mass ([#51](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/51),
[`93b5c9f`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/93b5c9ff0e0360b4c54d4bae603d0eb45198c8a3))
### Chores
- Re-release ([#52](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/52),
[`73b626d`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/73b626d954e67b57f6fdff470e9acbfb8a172370))
## v0.18.1 (2023-07-17)
### Bug Fixes
- Use sensor state data for scale sensors
([#50](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/50),
[`fcfa510`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/fcfa5100dfebb9dd451afdb7395eef530c7f3abb))
* fix: use sensor state data for scale sensors
* chore: improve coverage
## v0.18.0 (2023-07-16)
### Chores
- Bump python semantic release ([#49](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/49),
[`fa35709`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/fa357098f63c63478eb1fa83e2b220e67160430b))
### Features
- Add support for mi body composition scale 2
([#47](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/47),
[`3755aed`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/3755aede1da4e4a11c5534ef1b731a33f6f2b39c))
* feat: add support for mi body composition scale 2
* fix: correct scale device type
* test: add test for scale parser
## v0.17.2 (2023-06-03)
### Bug Fixes
- Jtyjgd03mmi is now detected as smoke sensor
([#46](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/46),
[`9e06702`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/9e0670252ebe9802fdf5eaec030f23813090da02))
## v0.17.1 (2023-05-21)
### Bug Fixes
- Add sleepy_device property ([#44](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/44),
[`fac1009`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/fac1009acdd4a31daa9e50eb6fffea6d0bff1702))
* fix: add sleepy_device property
* fix: shorten code
## v0.17.0 (2023-04-03)
### Features
- Add updated lywsd02mmc device type and mmc w505 body temperature sensor
([`23f85e2`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/23f85e29e887e042fe2eeea83b15ca2e1beeea08))
## v0.16.4 (2023-02-17)
### Bug Fixes
- Add jtyjgd03mi to sleepy devices ([#42](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/42),
[`bcf72a2`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/bcf72a21f95ba932f361b203bee9dc804776c15b))
## v0.16.3 (2023-02-09)
### Bug Fixes
- Add rtcgq02lm to sleepy devices ([#41](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/41),
[`899e084`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/899e0845211d1ab796a67379c0d99393828c02df))
## v0.16.2 (2023-02-07)
### Bug Fixes
- Add sleepy devices ([#40](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/40),
[`70137b4`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/70137b4addd9d67617de4d64464872f2a146928d))
* fix: add sleepy devices
* fix: use set for sleepy devices
## v0.16.1 (2023-02-05)
### Bug Fixes
- Relax bleak-retry-connector ([#38](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/38),
[`59abc8b`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/59abc8be1ea3e0a6c3c94353f54e5d131c155a11))
### Chores
- Add py311 to the CI ([#39](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/39),
[`642de25`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/642de25a941e1b729be643dd7e31aa83efd4431d))
## v0.16.0 (2023-02-05)
### Bug Fixes
- Bump python-semantic-release ([#37](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/37),
[`f2703fd`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/f2703fd49c0f4aafdba6983e0cf92bf4b2558882))
- Bump version
([`f9d9eb9`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/f9d9eb9656bde85124b4340cac79d1ccd8da748e))
- Force release ([#35](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/35),
[`5e9c736`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/5e9c736bfaa035a6418c59031ec6ab137fee5e06))
- Force release ([#36](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/36),
[`3f94b0f`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/3f94b0fd4449cd015858a6ed32426727a0be79a1))
- Revert version bump
([`51d208a`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/51d208ac8262d0d7af96ceb10b8c5856402f8ad4))
- Revert version bump
([`bd8f028`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/bd8f0284a9011bb45b94879152f66619f71fd72f))
- Update dependencies
([`e0bce32`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/e0bce3286b65c5e328c1b29ab82c7817fa51d24c))
* fix: update poetry and pyproject file
* fix: bump isort
* fix: bump isort in precommit
### Features
- Bump version
([`880f12d`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/880f12d39f18c07e291cc26d832cba65817a412f))
- Implement motion timeout
([`2818f1f`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/2818f1f6b25410168d59ca3f342801d222cee11e))
feat(RTCGQ02LM): implement motion timeout
## v0.15.0 (2023-01-04)
### Features
- Motion events ([#31](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/31),
[`2a52c1e`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/2a52c1eaafa02b7f00c649cebad0a3d304e5d15d))
## v0.14.3 (2023-01-03)
### Bug Fixes
- Assign unique keys to device class
([#30](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/30),
[`09a9fbb`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/09a9fbb0a088357ac585805bd1bb0db5def9c75c))
## v0.14.2 (2023-01-03)
### Bug Fixes
- Extend binary sensor enums ([#29](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/29),
[`4563a9b`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/4563a9bdf86bc1cd40dd86a141437bf20de1f7d6))
* fix: extend binary sensor enums
* fix: typo
* fix: add base device class
* fix: separate enum
## v0.14.1 (2023-01-02)
### Bug Fixes
- Lower bleak version requirement ([#28](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/28),
[`c9a80ac`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/c9a80ac1f6fcc433cd9499092c759d195309c2e4))
* fix: lower bleak dependency requirement
* fix: bleak dependency
## v0.14.0 (2023-01-01)
### Features
- Add new xiaomi sensors ([#27](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/27),
[`a39a5f9`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/a39a5f9c22fa7e497891bbaa147a4f756c922afa))
* feat: add new xiaomi sensors
## v0.13.0 (2023-01-01)
### Features
- Add door opening and motion sensors
([#26](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/26),
[`3e0babd`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/3e0babdde9a69aabd470506f908be55673708671))
* feat: add more binary sensors
* fix: fix tests and bump dependecies
* fix: add more tests
* feat: add problem sensors for opening and door
* feat: add motion sensors
* fix: remove mode sensor for now
* fix: do not use predefined sensors
* fix: remove mue4094rt
## v0.12.2 (2022-11-15)
### Bug Fixes
- Bump sensor-state-data ([#24](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/24),
[`c2dd39c`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/c2dd39ca898910c6458f90033068d8924ecadf51))
## v0.12.1 (2022-11-13)
### Bug Fixes
- Unit of measurement formaldehyde ([#23](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/23),
[`48035a1`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/48035a1dd704c23d1b8e74be0c7342d7d7da61b7))
## v0.12.0 (2022-11-12)
### Features
- Add support for HHCCJCY10 ([#21](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/21),
[`0f387c2`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/0f387c244644a8469de935ce4e5800d882868d81))
* feat: add support for HHCCJCY10
* feat: add new device_classes
Co-authored-by: Ernst Klamer
## v0.11.0 (2022-11-09)
### Features
- Add linptech ms1bb and hs1bb
([`52a21dd`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/52a21dd062d9a2172c8e23008f1ee80bbef6ce5e))
* feat: add linptech m1sbb
* fix: fix test m1sbb
* fix: use bind key in m1sbb test
* fix: improve test coverage
* fix: fix button test
* fix: formatting
* Add HS1BB(MI) motion sensor
* fix: formatting issues
* fix: linting issue
## v0.10.0 (2022-09-13)
### Features
- Update for bleak 0.17 support ([#19](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/19),
[`db14912`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/db14912f6c57f20363e85d49fe320765606e7ed3))
## v0.9.3 (2022-09-12)
### Bug Fixes
- Bump bleak-retry-connector ([#18](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/18),
[`08fc5aa`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/08fc5aab0e6cf2e772cff962c7cf801328ef66e9))
## v0.9.2 (2022-08-25)
### Bug Fixes
- Use short_address from bluetooth_data_tools
([#16](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/16),
[`d370cca`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/d370cca23c4c585437de5946d33383a550d06bc3))
## v0.9.1 (2022-08-16)
### Bug Fixes
- Rename Smart Door Lock E ([#13](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/13),
[`ef11f5f`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/ef11f5fd799e9a67223b8dbe5569a33c3e214bc0))
## v0.9.0 (2022-08-11)
### Features
- Intial support for binary_sensor devices
([#12](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/12),
[`fc0ff14`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/fc0ff143d695beedd190cc002e1173c8c17f5783))
## v0.8.2 (2022-08-11)
### Bug Fixes
- Set title and device name to something useful
([`73c4d4b`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/73c4d4b59f6729f9806bdf85d2544698840f4198))
### Chores
- Black tests
([`09c0b0e`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/09c0b0e743ddbf78c3324ad769a1d11d202e919f))
## v0.8.1 (2022-08-11)
### Bug Fixes
- Never poll if still pending
([`f617708`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/f617708e4f67d0a6a4d0b853efee5568a14f8aba))
### Chores
- Lint
([`346ceae`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/346ceae4e2fed90991ab663d319e341ff9947423))
## v0.8.0 (2022-08-06)
### Features
- Support polling MiFlora battery ([#11](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/11),
[`f08707b`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/f08707bd45d250a44ea7ca5da059feae61851089))
## v0.7.0 (2022-08-01)
### Features
- Add XMZNMSBMCN03 ([#9](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/9),
[`7ff0d49`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/7ff0d49f73f8150e520aaa77868a4f1bd65363b1))
## v0.6.4 (2022-08-01)
### Bug Fixes
- Refactor tests to avoid mypy failure
([`c1456d7`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/c1456d7fd96300ffc7bcc21c7d17d6ec63e7a287))
- Track last full service info so that we can quickly reauth the bindkey
([`c4feb20`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/c4feb20468fdfdd1a9c80c0bee77bef2a250d462))
## v0.6.3 (2022-08-01)
### Bug Fixes
- Unset bindkey_verified if bindkey starts to fail
([`d2fc9eb`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/d2fc9eb36289a0c4779e97439e682016ae0de1fc))
## v0.6.2 (2022-07-28)
### Bug Fixes
- Track whether or not we have seen a packet with a payload (HA75833)
([#8](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/8),
[`c99f9a2`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/c99f9a2cbd09ac6cebd31349d49d6f038bfea0e6))
## v0.6.1 (2022-07-27)
### Bug Fixes
- Voltage sensor should have a device class
([`600bb78`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/600bb7819752b6f742e515eea177b65c83b1d3dc))
### Chores
- Lint fixes
([`667239b`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/667239b568d324a7f552467c040b1529f97a3ae2))
## v0.6.0 (2022-07-25)
### Features
- Add Formaldehyde, Consumable and Voltage sensor
([`8292de7`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/8292de7425597897d562ea43380af221c661df90))
## v0.5.2 (2022-07-25)
### Bug Fixes
- Add special casing for when illumination is used for a binary sensor instead of a sensor
([`59c5729`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/59c5729b4c2f39944d61ab65c3aee40c6d041d21))
## v0.5.1 (2022-07-24)
### Bug Fixes
- Unset bindkey_verified on legacy devices if payload is corrupt as may be wrong key
([`f57827a`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/f57827a7caa0c9c6721f1d9773e89f3472ccf5c7))
## v0.5.0 (2022-07-24)
### Features
- New bindkey_verified variable to track whether encryption is working
([`dc622a5`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/dc622a511e1909d96d706b77ff86c8ad1905bda0))
## v0.4.1 (2022-07-24)
### Bug Fixes
- Hide encrypted devices on macOS where we don't know MAC address (for now)
([`20d0e62`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/20d0e62939907b8d58888cd346bda5f3365f943c))
## v0.4.0 (2022-07-23)
### Features
- Expose type of encryption to use on XiaomiBluetoothDeviceData
([`935482d`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/935482d0a78daf10b6e2ff772d32d195eba923ea))
## v0.3.0 (2022-07-23)
### Features
- Add lux sensor to cgpr1 ([#5](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/5),
[`accb30e`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/accb30eaa5dc710518e02b92bbfa1ed6c495c992))
## v0.2.0 (2022-07-23)
### Bug Fixes
- Fix test regression
([`1f80374`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/1f80374c5c56049235ee55f1fc885b8da2cadbb6))
- Remove stray print
([`55d67d7`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/55d67d741c1c7dd8da14729619515e09680542e1))
### Chores
- Fix lints
([`7511a1a`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/7511a1a435d61f246aadf497cdf5529492a24cdb))
### Features
- Add support for more sensor types
([`0a87594`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/0a875940fdf7330d53a03f3ca7b7a489353396b6))
## v0.1.0 (2022-07-22)
### Features
- Add release notes for 0.0.5
([`2a1d8c5`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/2a1d8c510e31e82bbc22d191eb98efc9a3cf65e8))
- Add SU001-T ([#2](https://github.com/Bluetooth-Devices/xiaomi-ble/pull/2),
[`2281d03`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/2281d033323f8e78e5c2a192c8a900efe859308b))
- Bump version
([`01656a0`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/01656a03ae6ece3bbcf361bf14355894cf359f0d))
## v0.0.4 (2022-07-22)
### Bug Fixes
- Workaround not knowing MAC on macOS
([`adcb639`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/adcb63967dbdfcd502c5fd15c27c33ceca5c7638))
## v0.0.3 (2022-07-22)
### Bug Fixes
- Trim service uuid from start of service data
([`8c35dc1`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/8c35dc16cf3a782f045d567bdce26e5a3296bbf9))
## v0.0.2 (2022-07-22)
### Bug Fixes
- Re-export sensor state classes like other bluetooth helpers
([`b7b97ba`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/b7b97ba50963218d45d9d883179302aca08987ca))
## v0.0.1 (2022-07-22)
### Bug Fixes
- Get remaining tests working
([`8a042e4`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/8a042e4e2ef64151b6ad1db45ba9164d97a536cb))
- Give all tests right MAc, so validation passes on linux
([`da76a1d`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/da76a1dbad06e8eb0c990355d4b84dee3db23860))
- Use fromutctimestamp for stable tests
([`c1e574c`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/c1e574c7a3e3eed98ee061764efc37599b9c251b))
### Chores
- __init__ version number
([`af8812d`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/af8812d4307350fad8247a8d1aa97766b764d236))
- Fix flake8 E203
([`595c318`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/595c31884bca7f34212d5ea2c5798b4ea55613c3))
- Fix flake8 E501
([`986bb25`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/986bb25c2481cb5612314a05ccf4d4c8847d4085))
- More lint
([`0ef1f3b`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/0ef1f3bd6c08c45eb7aa6d4362b494dacaa5400c))
- More lint
([`59c6ade`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/59c6ade0a6623683c5e7213493db9c8b30d43bd6))
- More lint
([`49e4ad5`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/49e4ad5c6c8db0d10f5bec74a51140c9b90be089))
- More lint
([`43f5d82`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/43f5d82cb0619727d8728cdb6e674e0fcf02ed8e))
- More mypy cleanup
([`2b65b45`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/2b65b4565439092c512113e2ee7ce509c08fe356))
- Reset changelog
([`bfe7793`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/bfe77939cd02c26e82d338b6987df0741038e3b0))
- Reset version number
([`dec6b69`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/dec6b69f60781ec37a4fabf0352b194c0a12818d))
- Run isort
([`890b72d`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/890b72d25e0e78e14a3fa35dd4c40b837adad731))
- Spelling
([`bf2c604`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/bf2c6049df43e8256a9b28235876dc3b390740cb))
- Spelling
([`483b17b`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/483b17b0b5f7d6c7ee7502f67a2731552ba9d2d1))
- Start mypy cleanup
([`88a13e9`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/88a13e9debe3b54bc743c23fae4cc6e343f549b1))
- Upgrade sensor-state-data>=2.0
([`59f2d00`](https://github.com/Bluetooth-Devices/xiaomi-ble/commit/59f2d0056b52238e02343c3d70d57eda3d1c2301))
Bluetooth-Devices-xiaomi-ble-741c848/CONTRIBUTING.md 0000664 0000000 0000000 00000007426 15222637367 0021626 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
Xiaomi BLE could always use more documentation, whether as part of the official Xiaomi BLE 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/xiaomi-ble.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/xiaomi-ble/issues
Bluetooth-Devices-xiaomi-ble-741c848/LICENSE 0000664 0000000 0000000 00000026121 15222637367 0020373 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-Devices-xiaomi-ble-741c848/README.md 0000664 0000000 0000000 00000007005 15222637367 0020645 0 ustar 00root root 0000000 0000000 # Xiaomi BLE
Manage Xiaomi BLE devices
## Installation
Install this via pip (or your favourite package manager).
`pip install xiaomi-ble`
## 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.
Special thanks to Piotr Machowski for the cloud key extractor basis.
Bluetooth-Devices-xiaomi-ble-741c848/commitlint.config.mjs 0000664 0000000 0000000 00000000362 15222637367 0023523 0 ustar 00root root 0000000 0000000 export default {
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-Devices-xiaomi-ble-741c848/docs/ 0000775 0000000 0000000 00000000000 15222637367 0020314 5 ustar 00root root 0000000 0000000 Bluetooth-Devices-xiaomi-ble-741c848/docs/Makefile 0000664 0000000 0000000 00000001175 15222637367 0021760 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-Devices-xiaomi-ble-741c848/docs/make.bat 0000664 0000000 0000000 00000001374 15222637367 0021726 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-Devices-xiaomi-ble-741c848/docs/source/ 0000775 0000000 0000000 00000000000 15222637367 0021614 5 ustar 00root root 0000000 0000000 Bluetooth-Devices-xiaomi-ble-741c848/docs/source/_static/ 0000775 0000000 0000000 00000000000 15222637367 0023242 5 ustar 00root root 0000000 0000000 Bluetooth-Devices-xiaomi-ble-741c848/docs/source/_static/.gitkeep 0000664 0000000 0000000 00000000000 15222637367 0024661 0 ustar 00root root 0000000 0000000 Bluetooth-Devices-xiaomi-ble-741c848/docs/source/changelog.md 0000664 0000000 0000000 00000000045 15222637367 0024064 0 ustar 00root root 0000000 0000000 ```{include} ../../CHANGELOG.md
```
Bluetooth-Devices-xiaomi-ble-741c848/docs/source/conf.py 0000664 0000000 0000000 00000003652 15222637367 0023121 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 = "Xiaomi BLE"
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-Devices-xiaomi-ble-741c848/docs/source/contributing.md 0000664 0000000 0000000 00000000050 15222637367 0024640 0 ustar 00root root 0000000 0000000 ```{include} ../../CONTRIBUTING.md
```
Bluetooth-Devices-xiaomi-ble-741c848/docs/source/index.md 0000664 0000000 0000000 00000000352 15222637367 0023245 0 ustar 00root root 0000000 0000000 # Welcome to Xiaomi BLE documentation!
```{toctree}
:caption: Installation & Usage
:maxdepth: 2
installation
usage
```
```{toctree}
:caption: Project Info
:maxdepth: 2
changelog
contributing
```
```{include} ../../README.md
```
Bluetooth-Devices-xiaomi-ble-741c848/docs/source/installation.md 0000664 0000000 0000000 00000000265 15222637367 0024642 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 xiaomi-ble
```
Bluetooth-Devices-xiaomi-ble-741c848/docs/source/usage.md 0000664 0000000 0000000 00000000140 15222637367 0023235 0 ustar 00root root 0000000 0000000 # Usage
To use this package, import it:
```python
import xiaomi_ble
```
TODO: Document usage
Bluetooth-Devices-xiaomi-ble-741c848/extract_tokens.py 0000664 0000000 0000000 00000004305 15222637367 0022775 0 ustar 00root root 0000000 0000000 import asyncio
import pprint
from getpass import getpass
from aiohttp import ClientSession
from xiaomi_ble import XiaomiCloudTokenFetch
from xiaomi_ble.cloud import SERVERS
# Adapted from PiotrMachowski's Xiaomi-cloud-tokens-extractor
# MIT License
#
# Copyright (c) 2020 Piotr Machowski
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
async def main(username: str, password: str, mac: str, servers: list[str]) -> None:
async with ClientSession() as session:
fetcher = XiaomiCloudTokenFetch(username, password, session)
device_info = await fetcher.get_device_info(mac, servers)
if device_info:
pprint.pprint(device_info)
return
print(f"No devices found matching the provided MAC address: {mac}.")
print("Username (email or user ID):")
username = input()
print("Password:")
password = getpass("")
print("Mac address:")
mac = input()
print(f"Server (one of: {','.join(SERVERS)}) Leave empty to check all available:")
server = input()
while server not in ["", *SERVERS]:
print(f"Invalid server provided. Valid values: {','.join(SERVERS)}")
print("Server:")
server = input()
print()
asyncio.run(main(username, password, mac, [server] if server else SERVERS))
Bluetooth-Devices-xiaomi-ble-741c848/poetry.lock 0000664 0000000 0000000 00001231001 15222637367 0021556 0 ustar 00root root 0000000 0000000 # This file is automatically @generated by Poetry 2.4.1 and should not be changed by hand.
[[package]]
name = "aiohappyeyeballs"
version = "2.6.2"
description = "Happy Eyeballs for asyncio"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "aiohappyeyeballs-2.6.2-py3-none-any.whl", hash = "sha256:4708045e2d7a6c6bdf8aafa8ed39649eaf926a4543b54560659129e3365953c4"},
{file = "aiohappyeyeballs-2.6.2.tar.gz", hash = "sha256:e202810ee718bd01fc6ef49e8ea53d023d5cb6b581076d7925aa499fa55dbe64"},
]
[[package]]
name = "aiohttp"
version = "3.14.1"
description = "Async http client/server framework (asyncio)"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "aiohttp-3.14.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8f6bb621e5863cfe8fe5ff5468002d200ec31f30f1280b259dc505b02595099e"},
{file = "aiohttp-3.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4f7215cb3933784f79ed20e5f050e15984f390424339b22375d5a53c933a0491"},
{file = "aiohttp-3.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d9d4e294455b23a68c9b8f042d0e8e377a265bcb15332753695f6e5b6819e0ce"},
{file = "aiohttp-3.14.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b238af795833d5731d049d82bc84b768ae6f8f97f0495963b3ed9935c5901cc3"},
{file = "aiohttp-3.14.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e4e5e0ae56914ecdbf446493addefc0159053dd53962cef37d7839f37f73d505"},
{file = "aiohttp-3.14.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:092e4ce3619a7c6dee52a6bdabda973d9b34b66781f840ce93c7e0cec30cf521"},
{file = "aiohttp-3.14.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb33777ea21e8b7ecde0e6fc84f598be0a1192eab1a63bc746d75aa75d38e7bd"},
{file = "aiohttp-3.14.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23119f8fd4f5d16902ed459b63b100bcd269628075162bddac56cc7b5273b3fb"},
{file = "aiohttp-3.14.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:57fc6745a4b7d0f5a9eb4f40a69718be6c0bc1b8368cc9fe89e90118719f4f42"},
{file = "aiohttp-3.14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6fd35beba67c4183b09375c5fff9accb47524191a244a99f95fd4472f5402c2b"},
{file = "aiohttp-3.14.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:672b9d65f42eb877f5c3f234a4547e4e1a226ca8c2eed879bb34670a0ce51192"},
{file = "aiohttp-3.14.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:24ba13339fed9251d9b1a1bec8c7ab84c0d1675d79d33501e11f94f8b9a84e05"},
{file = "aiohttp-3.14.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:94da27378da0610e341c4d30de29a191672683cc82b8f9556e8f7c7212a020fe"},
{file = "aiohttp-3.14.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:52cdac9432d8b4a719f35094a818d95adcae0f0b4fe9b9b921909e0c87de9e7d"},
{file = "aiohttp-3.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:672ac254412a24d0d0cf00a9e6c238877e4be5e5fa2d188832c1244f45f31966"},
{file = "aiohttp-3.14.1-cp310-cp310-win32.whl", hash = "sha256:2fe3607e71acc6ebb0ec8e492a247bf7a291226192dc0084236dfc12478916f6"},
{file = "aiohttp-3.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:30099eda75a53c32efb0920e9c33c195314d2cc1c680fbfd30894932ac5f27df"},
{file = "aiohttp-3.14.1-cp310-cp310-win_arm64.whl", hash = "sha256:5a837f49d901f9e368651b676912bff1104ed8c1a83b280bcd7b29adccef5c9c"},
{file = "aiohttp-3.14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa00140699487bd435fde4342d85c94cb256b7cd3a5b9c3396c67f19922afda2"},
{file = "aiohttp-3.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1c1af67559445498b502030c35c59db59966f47041ca9de5b4e707f86bd10b5f"},
{file = "aiohttp-3.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d44ec478e713ee7f29b439f7eb8dc2b9d4079e11ae114d2c2ac3d5daf30516c8"},
{file = "aiohttp-3.14.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d3b1a184a9a8f548a6b73f1e26b96b052193e4b3175ed7342aaf1151a1f00a04"},
{file = "aiohttp-3.14.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5f2504bc0322437c9a1ff6d3333ca56c7477b727c995f036b976ae17b98372c8"},
{file = "aiohttp-3.14.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73f05ea02013e02512c3bf42714f1208c57168c779cc6fe23516e4543089d0a6"},
{file = "aiohttp-3.14.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:797457503c2d426bee06eef808d07b31ede30b65e054444e7de64cad0061b7af"},
{file = "aiohttp-3.14.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b821a1f7dedf7e37450654e620038ac3b2e81e8fa6ea269337e97101978ec730"},
{file = "aiohttp-3.14.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4cd96b5ba05d67ed0cf00b5b405c8cd99586d8e3481e8ee0a831057591af7621"},
{file = "aiohttp-3.14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d459b98a932296c6f0e94f87511a0b1b90a8a02c30a50e60a297619cd5a58ee"},
{file = "aiohttp-3.14.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:764457a7be60825fb770a644852ff717bcbb5042f189f2bd16df61a81b3f6573"},
{file = "aiohttp-3.14.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f7a16ef45b081454ef844502d87a848876c490c4cb5c650c230f6ec79ed2c1e7"},
{file = "aiohttp-3.14.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2fbc3ed048b3475b9f0cbcb9978e9d2d3511acd91ead203af26ed9f0056004cf"},
{file = "aiohttp-3.14.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bedb0cd073cc2dc035e30aeb99444389d3cd2113afe4ef9fcd23d439f5bade85"},
{file = "aiohttp-3.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b6feea921016eb3d4e04d65fc4e9ca402d1a3801f562aef94989f54694917af3"},
{file = "aiohttp-3.14.1-cp311-cp311-win32.whl", hash = "sha256:313701e488100074ce99850404ee36e741abf6330179fec908a1944ecf570126"},
{file = "aiohttp-3.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:03ab4530fdcb3a543a122ba4b65ac9919da9fe9f78a03d328a6e38ff962f7aa5"},
{file = "aiohttp-3.14.1-cp311-cp311-win_arm64.whl", hash = "sha256:486f7d16ed54c39c2cbd7ca71fd8ba2b8bb7860df65bd7b6ed640bab96a38a8b"},
{file = "aiohttp-3.14.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d35143e27778b4bb0fb189562d7f275bff79c62ab8e98459717c0ea617ff2480"},
{file = "aiohttp-3.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bcfb80a2cc36fba2534e5e5b5264dc7ae6fcd9bf15256da3e53d2f499e6fa29d"},
{file = "aiohttp-3.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27fd7c91e51729b4f7e1577865fa6d34c9adccbc39aabe9000285b48af9f0ec2"},
{file = "aiohttp-3.14.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:64c567bf9eaf664280116a8688f63016e6b32db2505908e2bdaca1b6438142f2"},
{file = "aiohttp-3.14.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f5e6ff2bdbb8f4cd3fbe41f99e25bbcd58e3bf9f13d3dd31a11e7917251cc77a"},
{file = "aiohttp-3.14.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2f73e01dc37122325caf079982621262f96d74823c179038a82fddfc50359264"},
{file = "aiohttp-3.14.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb2c0c80d431c0d03f2c7dbf125150fedd4f0de17366a7ca33f7ccb822391842"},
{file = "aiohttp-3.14.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e6fc1a85fa7194a1a7d19f44e8609180f4a8eb5fa4c7ed8b4355f080fad235c"},
{file = "aiohttp-3.14.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:686b6c0d3911ec387b444ddf5dc62fb7f7c0a7d5186a7861626496a5ab4aff95"},
{file = "aiohttp-3.14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c6fa4dc7ad6f8109c70bb1499e589f76b0b792baf39f9b017eb92c8a81d0a199"},
{file = "aiohttp-3.14.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:87a5eea1b2a5e21e1ebdbb33ad4165359189327e63fc4e4894693e7f821ac817"},
{file = "aiohttp-3.14.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c1421eb01d4fd608d88cc8290211d177a58532b55ad94076fb349c5bf467f0a"},
{file = "aiohttp-3.14.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:34b257ec41345c1e8f2df68fa908a7952f5de932723871eb633ecbbff396c9a4"},
{file = "aiohttp-3.14.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:de538791a80e5d862addbc183f70f0158ac9b9bb872bb147f1fd2a683691e087"},
{file = "aiohttp-3.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6f71173be42d3241d428f760122febb748de0623f44308a6f120d0dd9ec572e3"},
{file = "aiohttp-3.14.1-cp312-cp312-win32.whl", hash = "sha256:ec8dc383ee57ea3e883477dcca3f11b65d58199f1080acaf4cd6ad9a99698be4"},
{file = "aiohttp-3.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2aa92c87868cd13674989f9ee83e5f9f7ea4237589b728048e1f0c8f6caa3271"},
{file = "aiohttp-3.14.1-cp312-cp312-win_arm64.whl", hash = "sha256:2c840c90759922cb5e6dda94596e079a30fb5a5ba548e7e0dc00574703940847"},
{file = "aiohttp-3.14.1-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:b3a03285a7f9c7b016324574a6d92a1c895da6b978cb8f1deee3ac72bc6da178"},
{file = "aiohttp-3.14.1-cp313-cp313-android_21_x86_64.whl", hash = "sha256:2a73f487ab8ef5abbb24b7aa9b73e98eaba9e9e031804ff2416f02eca315ccaf"},
{file = "aiohttp-3.14.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:915fbb7b41b115192259f8c9ae58f3ddc444d2b5579917270211858e606a4afd"},
{file = "aiohttp-3.14.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:7fb4bdf95b0561a79f259f9d28fbc109728c5ee7f27aff6391f0ca703a329abe"},
{file = "aiohttp-3.14.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:1b9748363260121d2927704f5d4fc498150669ca3ae93625986ee89c8f80dcd4"},
{file = "aiohttp-3.14.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:86a6dab78b0e43e2897a3bbe15745aa60dc5423ca437b7b0b164c069bf91b876"},
{file = "aiohttp-3.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4dfd6e47d3c44c2279907607f73a4240b88c69eb8b90da7e2441a8045dfd21da"},
{file = "aiohttp-3.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:317acd9f8602858dc7d59679812c376c7f0b97bcbbf16e0d6237f54141d8a8a6"},
{file = "aiohttp-3.14.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd869c427324e5cb15195793de951295710db28be7d818247f3097b4ab5d4b96"},
{file = "aiohttp-3.14.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:93b032b5ec3255473c143627d21a69ac74ae12f7f33974cb587c564d11b1066f"},
{file = "aiohttp-3.14.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f234b4deb12f3ad59127e037bc57c40c21e45b45282df7d3a55a0f409f595296"},
{file = "aiohttp-3.14.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9af6779bfb46abf124068327abcdf9ce95c9ef8287a3e8da76ccf2d0f16c28fa"},
{file = "aiohttp-3.14.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:faccab372e66bc76d5731525e7f1143c922271725b9d38c9f97edcc66266b451"},
{file = "aiohttp-3.14.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f380468b09d2a81633ee863b0ec5648d364bd17bb8ecfb8c2f387f7ac1faf42c"},
{file = "aiohttp-3.14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:97e704dcd26271f5bda3fa07c3ce0fb76d6d3f8659f4baa1a24442cc9ba177ca"},
{file = "aiohttp-3.14.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:269b76ac5394092b95bc4a098f4fc6c191c083c3bd12775d1e30e663132f6a09"},
{file = "aiohttp-3.14.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c0b3e614340c889d575451696374c9d17affd54cd607ca0babed8f8c37b9397"},
{file = "aiohttp-3.14.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:5663ee9257cfa1add7253a7da3035a02f31b6600ec48261585e1800a81533080"},
{file = "aiohttp-3.14.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:603a2c834142172ffddc054067f5ec0ca65d57a0aa98a71bc81952573208e345"},
{file = "aiohttp-3.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cb21957bb8aca671c1765e32f58164cf0c50e6bf41c0bbbd16da20732ecaf588"},
{file = "aiohttp-3.14.1-cp313-cp313-win32.whl", hash = "sha256:e509a55f681e6158c20f70f102f9cf61fb20fbc382272bc6d94b7343f2582780"},
{file = "aiohttp-3.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:1ac8531b638959718e18c2207fbfe297819875da46a740b29dfa29beba64355a"},
{file = "aiohttp-3.14.1-cp313-cp313-win_arm64.whl", hash = "sha256:250d14af67f6b6a1a4a811049b1afa69d61d617fca6bf33149b3ab1a6dbcf7b8"},
{file = "aiohttp-3.14.1-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:7c106c26852ca1c2047c6b80384f17100b4e439af276f21ef3d4e2f450ae7e15"},
{file = "aiohttp-3.14.1-cp314-cp314-android_24_x86_64.whl", hash = "sha256:20205f7f5ade7aaec9f4b500549bbc071b046453aed72f9c06dcab87896a83e8"},
{file = "aiohttp-3.14.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:62a759436b29e677181a9e76bab8b8f689a29cb9c535f45f7c48c9c830d3f8c3"},
{file = "aiohttp-3.14.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:2964cbf553df4d7a57348da44d961d871895fc1ee4e8c322b2a95612c7b17fba"},
{file = "aiohttp-3.14.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:237651caadc3a59badd39319c54642b5299e9cc98a3a194310e55d5bb9f5e397"},
{file = "aiohttp-3.14.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:896e12dfdbbab9d8f7e16d2b28c6769a60126fa92095d1ebf9473d02593a2448"},
{file = "aiohttp-3.14.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d03f281ed22579314ba00821ce20115a7c0ac430660b4cc05704a3f818b3e004"},
{file = "aiohttp-3.14.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:07eabb979d236335fed927e137a928c9adfb7df3b9ec7aa31726f133a62be983"},
{file = "aiohttp-3.14.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4fe1f1087cbadb280b5e1bb054a4f00d1423c74d6626c5e48400d871d34ecefe"},
{file = "aiohttp-3.14.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:367a9314fdc79dab0fac96e216cb41dd73c85bdca85306ce8999118ba7e0f333"},
{file = "aiohttp-3.14.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a24f677ebe83749039e7bdf862ff0bbb16818ae4193d4ef96505e269375bcce0"},
{file = "aiohttp-3.14.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c83afe0ba876be7e943d2e0ba645809ad441575d2840c895c21ee5de93b9377a"},
{file = "aiohttp-3.14.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:634e385930fb6d2d479cf3aa66515955863b77a5e3c2b5894ca259a25b308602"},
{file = "aiohttp-3.14.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eeea07c4397bbc57719c4eed8f9c284874d4f175f9b6d57f7a1546b976d455ca"},
{file = "aiohttp-3.14.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:335c0cc3e3545ce98dcb9cfcb836f40c3411f43fa03dab757597d80c89af8a35"},
{file = "aiohttp-3.14.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:ae6be797afdef264e8a84864a85b196ca06045586481b3df8a967322fd2fa844"},
{file = "aiohttp-3.14.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:8560b4d712474335d08907db7973f71912d3a9a8f1dee992ec06b5d2fe359496"},
{file = "aiohttp-3.14.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7edd08e0a5deb1e8564a2fcd8f4561014a3f05252334671bbf55ddd47db0e5"},
{file = "aiohttp-3.14.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:b6ff7fcee63287ae57b5df3e4f5957ce032122802509246dec1a5bcc55904c95"},
{file = "aiohttp-3.14.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6ffbb2f4ec1ceaff7e07d43922954da26b223d188bf30658e561b98e23089444"},
{file = "aiohttp-3.14.1-cp314-cp314-win32.whl", hash = "sha256:a9875b46d910cff3ea2f5962f9d266b465459fe634e22556ab9bd6fc1192eea0"},
{file = "aiohttp-3.14.1-cp314-cp314-win_amd64.whl", hash = "sha256:af8b4b81a960eeaf1234971ac3cd0ba5901f3cd42eae42a46b4d089a8b492719"},
{file = "aiohttp-3.14.1-cp314-cp314-win_arm64.whl", hash = "sha256:cf4491381b1b57425c315a56a439251b1bdac07b2275f19a8c44bc57744532ec"},
{file = "aiohttp-3.14.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:819c054312f1af92947e6a55883d1b66feefab11531a7fc45e0fb9b63880b5c2"},
{file = "aiohttp-3.14.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:10ee9c1753a8f706345b22496c79fbddb5be0599e0823f3738b1534058e25340"},
{file = "aiohttp-3.14.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1601cc37baf5750ccacae618ec2daf020769581695550e3b654a911f859c563d"},
{file = "aiohttp-3.14.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d6e0ac9da31c9c04c84e1c0182ad8d6df35965a85cae29cd71d089621b3ae94"},
{file = "aiohttp-3.14.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9e8f2d660c350b3d0e259c7a7e3d9b7fc8b41210cbcc3d4a7076ff0a5e5c2fdc"},
{file = "aiohttp-3.14.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4691802dda97be727f79d86818acaad7eb8e9252626a1d6b519fedbb92d5e251"},
{file = "aiohttp-3.14.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c389c482a7e9b9dc3ee2701ac46c4125297a3818875b9c305ddb603c04828fd1"},
{file = "aiohttp-3.14.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fc0cacab7ba4e56f0f81c82a98c09bed2f39c940107b03a34b168bdf7597edd3"},
{file = "aiohttp-3.14.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:979ed4717f59b8bb12e3963378fa285d93d367e15bcd66c721311826d3c44a6c"},
{file = "aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:38e1e7daaea81df51c952e18483f323d878499a1e2bfe564790e0f9701d6f203"},
{file = "aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:4132e72c608fe9fecb8f409113567605915b83e9bdd3ea56538d2f9cd35002f1"},
{file = "aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:eefd9cc9b6d4a2db5f00a26bc3e4f9acf71926a6ec557cd56c9c6f27c290b665"},
{file = "aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:b165790117eea512d7f3fb22f1f6dad3d55a7189571993eb015591c1401276d1"},
{file = "aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ed09c7eb1c391271c2ed0314a51903e72a3acb653d5ccfc264cdf3ef11f8269d"},
{file = "aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:99abd37084b82f5830c635fddd0b4993b9742a66eb746dacf433c8590e8f9e3c"},
{file = "aiohttp-3.14.1-cp314-cp314t-win32.whl", hash = "sha256:47ddf841cdecc810749921d25606dee45857d12d2ad5ddb7b5bd7eab12e4b365"},
{file = "aiohttp-3.14.1-cp314-cp314t-win_amd64.whl", hash = "sha256:5e78b522b7a6e27e0b25d19b247b75039ac4c94f99823e3c9e53ae1603a9f7e9"},
{file = "aiohttp-3.14.1-cp314-cp314t-win_arm64.whl", hash = "sha256:90d53f1609c29ccc2193945ef732428382a28f78d0456ae4d3daf0d48b74f0f6"},
{file = "aiohttp-3.14.1.tar.gz", hash = "sha256:307f2cff90a764d329e77040603fa032db89c5c24fdad50c4c15334cba744035"},
]
[package.dependencies]
aiohappyeyeballs = ">=2.5.0"
aiosignal = ">=1.4.0"
attrs = ">=17.3.0"
frozenlist = ">=1.1.1"
multidict = ">=4.5,<7.0"
propcache = ">=0.2.0"
typing_extensions = {version = ">=4.4", markers = "python_version < \"3.13\""}
yarl = ">=1.17.0,<2.0"
[package.extras]
speedups = ["Brotli (>=1.2) ; platform_python_implementation == \"CPython\" and sys_platform != \"android\" and sys_platform != \"ios\"", "aiodns (>=3.3.0) ; sys_platform != \"android\" and sys_platform != \"ios\"", "backports.zstd ; platform_python_implementation == \"CPython\" and python_version < \"3.14\" and sys_platform != \"android\" and sys_platform != \"ios\"", "brotlicffi (>=1.2) ; platform_python_implementation != \"CPython\""]
[[package]]
name = "aiooui"
version = "0.1.9"
description = "Async OUI lookups"
optional = false
python-versions = "<4.0,>=3.9"
groups = ["main"]
files = [
{file = "aiooui-0.1.9-cp310-cp310-manylinux_2_31_x86_64.whl", hash = "sha256:64d904b43f14dd1d8d9fcf1684d9e2f558bc5e0bd68dc10023c93355c9027907"},
{file = "aiooui-0.1.9-py3-none-any.whl", hash = "sha256:737a5e62d8726540218c2b70e5f966d9912121e4644f3d490daf8f3c18b182e5"},
{file = "aiooui-0.1.9.tar.gz", hash = "sha256:e8c8bc59ab352419e0747628b4cce7c4e04d492574c1971e223401126389c5d8"},
]
[[package]]
name = "aiosignal"
version = "1.4.0"
description = "aiosignal: a list of registered asynchronous callbacks"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e"},
{file = "aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7"},
]
[package.dependencies]
frozenlist = ">=1.1.0"
typing-extensions = {version = ">=4.2", markers = "python_version < \"3.13\""}
[[package]]
name = "alabaster"
version = "1.0.0"
description = "A light, configurable Sphinx theme"
optional = true
python-versions = ">=3.10"
groups = ["main"]
markers = "extra == \"docs\""
files = [
{file = "alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b"},
{file = "alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e"},
]
[[package]]
name = "ast-serialize"
version = "0.5.0"
description = "Python bindings for mypy AST serialization"
optional = false
python-versions = ">=3.7"
groups = ["dev"]
files = [
{file = "ast_serialize-0.5.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8f5c14f169eb0972c0c21bada5358b23d6047c76583b005234f865b11f1fa00a"},
{file = "ast_serialize-0.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7d1a2de9de5be04652f0ed60738356ef94f66db37924a9499fffe98dc491aa0b"},
{file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be5173fb66f9b49026d9d5a2ff0fc7c7009077107c0eb285b2d60fdf1fe10bd1"},
{file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f8015cd071ac1339924ee2b8098c93e00e155f30a16f40ec9816fcf84f4753f6"},
{file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5499e8797edff2a9186aa313ed382c6b422e798e9332d9953badcee6e69a88f2"},
{file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6848f2a093fb5548751a9a09bff8fcd229e2bbeb0e3331f391b6ae6d26cd9903"},
{file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:832d4c998e0b091fd60a6d6bceee535483c4d490de9ba85003af835225719261"},
{file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:16db7c62ec0b8efe1d7afd283a388d8f74f2605d56032e5a37747d2de8dba027"},
{file = "ast_serialize-0.5.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:baf5eb061eb5bccade4128ad42da33787d72f6013809cd1b590376ece8b3c937"},
{file = "ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:104e4a35bd7c124173c41760ef9aaea17ddb3f86c65cb643671d59afbe3ee94c"},
{file = "ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:36be371028fc1675acb38a331bde160dbab7ff907fdf00b67eb6911aa106951b"},
{file = "ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:061ee58bdb52341c8201a6df41182a977736bae3b7ded87ca7176ca25a8a47ab"},
{file = "ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b15219e9cdc9f53f6f4cb51c009203507228226148c05c5e8fe451c28b435eb3"},
{file = "ast_serialize-0.5.0-cp314-cp314t-win32.whl", hash = "sha256:842d1c004bb466c7df036f95fabef789570541922b10976b12f5592a69cf0b38"},
{file = "ast_serialize-0.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b0c06d760909b095cc466356dfccd05a1c7233a6ca191c020dca2c6a6f16c24c"},
{file = "ast_serialize-0.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:787baedb0262cc49e8ce37cc15c00ae818e46a165a3b36f5e21ed174998104cb"},
{file = "ast_serialize-0.5.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:0668aa9459cfa8c9c49ddd2163ebcf43088ba045ef7492af6fe22e0098303101"},
{file = "ast_serialize-0.5.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:bf683d6363edf2b39eed6b6d4fe22d34b6203867a67e27134d9e2a2680c4bc4a"},
{file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc22cf0c9be65e71cf88fda130af60d61eb4a79370ad4cfe7900d48a4aa2211"},
{file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f66173891548c9f2726bf27957b41cabce12fa679dc6da505ddbde4d4b3b31cf"},
{file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e42d729ef2be96a14efbad355093284739e3670ece3e534f82cc8832790911d9"},
{file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b725026bafa801dbd7310eb13a75f0a2e370e7e51b2cb225f9d21fcfadf919ee"},
{file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b54f60c1d78767a53b67eaa663f0dfac3afe606aa07f1301572f588b73d64809"},
{file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:27d51654fc240a1e87e742d353d98eb45b75f62f129086b3596ab53df2ac2a43"},
{file = "ast_serialize-0.5.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c36237c46dd1674542f2109740ea5ea485a169bf1431939ada0434e17934"},
{file = "ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1943db345233cc7194a470f13afa9c59772c0b123dea0c9414c4d4ca54369759"},
{file = "ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:df1c00022cbbcb064bfaa505aa9c9295362443ce5dacb459d1331d3da353f887"},
{file = "ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:cae65289fc456fde04af979a2be09302ef5d8ab92ef23e596d6746dc267ada27"},
{file = "ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:239a4c354e8d676e9d94631d1d4a64edc6b266f86ff3a5a80aedd344f342c01d"},
{file = "ast_serialize-0.5.0-cp39-abi3-win32.whl", hash = "sha256:143a4ef63285a075871908fda3672dc21864b83a8ec3ee12304aa3e4c5387b9a"},
{file = "ast_serialize-0.5.0-cp39-abi3-win_amd64.whl", hash = "sha256:cf25572c526add400f26a4750dc6ce0c3bb93fc1f75e7ae0cad4ce4f2cd5c590"},
{file = "ast_serialize-0.5.0-cp39-abi3-win_arm64.whl", hash = "sha256:92a31c9c20d25a076edaeec76b128a3535d74a24f340b9a8a7e96c9b86dc9642"},
{file = "ast_serialize-0.5.0.tar.gz", hash = "sha256:5880091bfe6f4f986f22866375c2e884843e7a0b6343ae41aeea659613d879b6"},
]
[[package]]
name = "async-interrupt"
version = "1.2.2"
description = "Context manager to raise an exception when a future is done"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "async_interrupt-1.2.2-py3-none-any.whl", hash = "sha256:0a8deb884acfb5fe55188a693ae8a4381bbbd2cb6e670dac83869489513eec2c"},
{file = "async_interrupt-1.2.2.tar.gz", hash = "sha256:be4331a029b8625777905376a6dc1370984c8c810f30b79703f3ee039d262bf7"},
]
[[package]]
name = "attrs"
version = "26.1.0"
description = "Classes Without Boilerplate"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309"},
{file = "attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32"},
]
[[package]]
name = "babel"
version = "2.18.0"
description = "Internationalization utilities"
optional = true
python-versions = ">=3.8"
groups = ["main"]
markers = "extra == \"docs\""
files = [
{file = "babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35"},
{file = "babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d"},
]
[package.extras]
dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata ; sys_platform == \"win32\""]
[[package]]
name = "black"
version = "26.5.1"
description = "The uncompromising code formatter."
optional = false
python-versions = ">=3.10"
groups = ["dev"]
files = [
{file = "black-26.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9942db8888e06943c5dde66ca0037dcff82a2a4ec1ad0ada9e0d2ee9d9823893"},
{file = "black-26.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:89c93167a74d3a75dfaa38a5c7cca015537d5820dd7f17d63267d674a61cae90"},
{file = "black-26.5.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22f2cd76d069cc54c71f10360744ba8983fbb616903b4304a85b734915c8e1b4"},
{file = "black-26.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:87ed5c6f450580a2f6790bc7cbfb016dfc73bc750249762268a3695361315eef"},
{file = "black-26.5.1-cp310-cp310-win_arm64.whl", hash = "sha256:58b4bd92cf88aacf83d88479c8f9caee044b1ec55f2451a337354a7ea2590a22"},
{file = "black-26.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:96ae2c733b2aabdd9986e2c5df628ff3473676cd1c5faded1ff496cf6d74083c"},
{file = "black-26.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0e48b87e03bf109288e55cfceadcfa15ff5470aca2851a851950ed2926f450d7"},
{file = "black-26.5.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5119fa92ae61f786e8c3662fd60aece1d0a2dd5cca5d0c79417a95e7a4272a59"},
{file = "black-26.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:30d3c14661f2792e9142cce3eeeb1cbc175b3eb5f733be0c8eeb99651e52b0c3"},
{file = "black-26.5.1-cp311-cp311-win_arm64.whl", hash = "sha256:1ef92b76f7733f282fd096ea406200b5a286c42947412b0eaff3a74e3616cefe"},
{file = "black-26.5.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4ad6fa01f941920f54f2bbb35f3df7673428a0ef98a0b0840c2eaef3b110efa8"},
{file = "black-26.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3915f256e75a2d7cf88d8953d37f780455dc586cc72dee059c528fe77f581217"},
{file = "black-26.5.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d98d4137277c75dfb898ec8d846c4fd68ba1e9cf77f95e2865c203dc18f4c3d"},
{file = "black-26.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:a1dca32d9f1784af512a13410ec204c6f7f0aa9797a111c42e1c03449821c264"},
{file = "black-26.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:1037d5ac7b7b310b2632ad867ec8d0e4c4819dcdb0b820f63135da746a24e418"},
{file = "black-26.5.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b36cf2ddf5566e205f6535f782a62194a184d33e175b64ae8c40b1737522be3"},
{file = "black-26.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f7ea64ebfa01b50f693508fc39f875e264446d3b097088f84f203b9d09618a0"},
{file = "black-26.5.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecb3e624844c798144e9bd986954e0adc81d8911a1f30f375e1252fe26e8c294"},
{file = "black-26.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:e1a26503279b6b310669fb0b219c39e4820b77e8189fe80f522bb511f247db0a"},
{file = "black-26.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:5c34b25da232ead53a6f335b76dbea124f4d152ad568b9080d6f944bc2b34b52"},
{file = "black-26.5.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e88976690a64b0af98312ca958415849cb42423423c5f2ee74af4b49a97a2168"},
{file = "black-26.5.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32d5ea7f6c8bdfa6e648326ebca1f02b0764e2a029edc6f8dce2627e19d468c3"},
{file = "black-26.5.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ea8d16dc41655aa113cd64665e7219446cd7e4ff2248d7178eaa905190c86b18"},
{file = "black-26.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:577f21094ea469ef92ec1adaf2c9441a226d2144d01a5be2fa823cecf6543e50"},
{file = "black-26.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:ed1a20af114c301a0269bf01163d51dbef72737fd65f850001e7cbe7f3c7abae"},
{file = "black-26.5.1-py3-none-any.whl", hash = "sha256:4ed7f7da04046d2e488437170797d3b4a4ad83906683bcb7dfc68b673bbce5e2"},
{file = "black-26.5.1.tar.gz", hash = "sha256:dd321f668053961824bcc1be1cc1df748b2d7e4fa28086b08331e577b0100a73"},
]
[package.dependencies]
click = ">=8.0.0"
mypy-extensions = ">=0.4.3"
packaging = ">=22.0"
pathspec = ">=1.0.0"
platformdirs = ">=2"
pytokens = ">=0.4.0,<0.5.0"
[package.extras]
colorama = ["colorama (>=0.4.3)"]
d = ["aiohttp (>=3.10)"]
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
uvloop = ["uvloop (>=0.15.2) ; sys_platform != \"win32\"", "winloop (>=0.5.0) ; sys_platform == \"win32\""]
[[package]]
name = "bleak"
version = "3.0.2"
description = "Bluetooth Low Energy platform Agnostic Klient"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "bleak-3.0.2-py3-none-any.whl", hash = "sha256:39092feb9e83f1df5ad2f88e837723c7211c982ce9e9cda6235104bc2ebe0d0d"},
{file = "bleak-3.0.2.tar.gz", hash = "sha256:c2229cb8238d5876b4bd05c74bf7a1aea1f88da39d2e51ac9dfd5cc319d5265f"},
]
[package.dependencies]
dbus-fast = {version = ">=1.83.0", markers = "sys_platform == \"linux\""}
pyobjc-core = {version = ">=10.3", markers = "sys_platform == \"darwin\""}
pyobjc-framework-corebluetooth = {version = ">=10.3", markers = "sys_platform == \"darwin\""}
pyobjc-framework-libdispatch = {version = ">=10.3", markers = "sys_platform == \"darwin\""}
typing-extensions = {version = ">=4.7.0", markers = "python_full_version < \"3.12.0\""}
winrt-runtime = {version = ">=3.1", markers = "sys_platform == \"win32\""}
winrt-windows-devices-bluetooth = {version = ">=3.1", markers = "sys_platform == \"win32\""}
winrt-windows-devices-bluetooth-advertisement = {version = ">=3.1", markers = "sys_platform == \"win32\""}
winrt-windows-devices-bluetooth-genericattributeprofile = {version = ">=3.1", markers = "sys_platform == \"win32\""}
winrt-windows-devices-enumeration = {version = ">=3.1", markers = "sys_platform == \"win32\""}
winrt-windows-devices-radios = {version = ">=3.1", markers = "sys_platform == \"win32\""}
winrt-windows-foundation = {version = ">=3.1", markers = "sys_platform == \"win32\""}
winrt-windows-foundation-collections = {version = ">=3.1", markers = "sys_platform == \"win32\""}
winrt-windows-storage-streams = {version = ">=3.1", markers = "sys_platform == \"win32\""}
[package.extras]
pythonista = ["bleak-pythonista (>=0.1.1)"]
[[package]]
name = "bleak-retry-connector"
version = "4.6.1"
description = "A connector for Bleak Clients that handles transient connection failures"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "bleak_retry_connector-4.6.1-py3-none-any.whl", hash = "sha256:018ba421babe05785e5a6497c73f3894772fad0f7fa5b054d48beb3d180ce0c4"},
{file = "bleak_retry_connector-4.6.1.tar.gz", hash = "sha256:ac2d19362f96757708dff2b0fedfefd5a8d8efb724027a777e54cc8ac2fc5a3d"},
]
[package.dependencies]
bleak = ">=2"
bluetooth-adapters = {version = ">=0.15.2", markers = "platform_system == \"Linux\""}
dbus-fast = {version = ">=4.3.0", markers = "platform_system == \"Linux\""}
[[package]]
name = "bluetooth-adapters"
version = "2.4.0"
description = "Tools to enumerate and find Bluetooth Adapters"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "bluetooth_adapters-2.4.0-py3-none-any.whl", hash = "sha256:f2841c49e8750658ecb8c1d46d2c05a9de4740362eb83e3e7e501d3092c0377e"},
{file = "bluetooth_adapters-2.4.0.tar.gz", hash = "sha256:e8a61935a867deb8af981e986bdf504c4398895ff254eac626790184a4fdd1e3"},
]
[package.dependencies]
aiooui = ">=0.1.1"
bleak = ">=3.0.2"
dbus-fast = {version = ">=5.0.14", markers = "platform_system == \"Linux\""}
uart-devices = ">=0.1.0"
usb-devices = ">=0.4.5"
[package.extras]
docs = ["Sphinx (>=8.1.3,<9)", "myst-parser (>=4.0.1,<4.1)", "sphinx-rtd-theme (>=3.1.0,<4)"]
[[package]]
name = "bluetooth-auto-recovery"
version = "1.6.4"
description = "Recover bluetooth adapters that are in an stuck state"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "bluetooth_auto_recovery-1.6.4-py3-none-any.whl", hash = "sha256:39485c41e17a2d4887c1fbf04b4e2fd37f0f3c7898db388753e026ca3addf055"},
{file = "bluetooth_auto_recovery-1.6.4.tar.gz", hash = "sha256:c69a9f3b5e00239ab005d808aa5e7afa3c36a82f86e9531b6c7682bde1bc3ecc"},
]
[package.dependencies]
bluetooth-adapters = ">=0.16.0"
btsocket = ">=0.2.0"
PyRIC = ">=0.1.6.3"
usb-devices = ">=0.4.1"
[package.extras]
docs = ["Sphinx (>=5,<8)", "myst-parser (>=0.18,<3.1)", "sphinx-rtd-theme (>=1,<4)"]
[[package]]
name = "bluetooth-data-tools"
version = "1.29.18"
description = "Tools for converting bluetooth data and packets"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "bluetooth_data_tools-1.29.18-cp314-cp314-manylinux_2_41_x86_64.whl", hash = "sha256:fd5408d54eac9f5ecf34193a40e1badb905ae8b3a1801b92252e365064888c11"},
{file = "bluetooth_data_tools-1.29.18.tar.gz", hash = "sha256:87f678cc7b4963cb3ba73064dd72155f915bec4b21f22acd997848ddc0b1c67b"},
]
[package.dependencies]
cryptography = ">=47.0.0"
[package.extras]
docs = ["Sphinx (>=5,<9)", "myst-parser (>=0.18,<4.1)", "sphinx-rtd-theme (>=1,<4)"]
[[package]]
name = "bluetooth-sensor-state-data"
version = "1.9.0"
description = "Models for storing and converting Bluetooth Sensor State Data"
optional = false
python-versions = ">=3.11"
groups = ["main"]
files = [
{file = "bluetooth_sensor_state_data-1.9.0-py3-none-any.whl", hash = "sha256:adf3b17774011f188b8002c54b669e24cd60d3332ef42facd18baa60c56077e3"},
{file = "bluetooth_sensor_state_data-1.9.0.tar.gz", hash = "sha256:d289b86e4a65a5bccce6310f686d6f3e4ac4a2c56a13c2fccb475a6894667139"},
]
[package.dependencies]
bluetooth-data-tools = ">=1.28.0"
habluetooth = ">=3.42.0"
sensor-state-data = ">=2.0"
[package.extras]
docs = ["Sphinx (>=5,<8)", "myst-parser (>=0.18,<4.1)", "sphinx-rtd-theme (>=1,<4)"]
[[package]]
name = "btsocket"
version = "0.3.0"
description = "Python library for BlueZ Bluetooth Management API"
optional = false
python-versions = "*"
groups = ["main"]
files = [
{file = "btsocket-0.3.0-py2.py3-none-any.whl", hash = "sha256:949821c1b580a88e73804ad610f5173d6ae258e7b4e389da4f94d614344f1a9c"},
{file = "btsocket-0.3.0.tar.gz", hash = "sha256:7ea495de0ff883f0d9f8eea59c72ca7fed492994df668fe476b84d814a147a0d"},
]
[package.extras]
dev = ["bumpversion", "coverage", "pycodestyle", "pygments", "sphinx", "sphinx-rtd-theme", "twine"]
docs = ["pygments", "sphinx", "sphinx-rtd-theme"]
rel = ["bumpversion", "twine"]
test = ["coverage", "pycodestyle"]
[[package]]
name = "certifi"
version = "2026.5.20"
description = "Python package for providing Mozilla's CA Bundle."
optional = true
python-versions = ">=3.7"
groups = ["main"]
markers = "extra == \"docs\""
files = [
{file = "certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897"},
{file = "certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d"},
]
[[package]]
name = "cffi"
version = "2.0.0"
description = "Foreign Function Interface for Python calling C code."
optional = false
python-versions = ">=3.9"
groups = ["main"]
markers = "platform_python_implementation != \"PyPy\""
files = [
{file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"},
{file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"},
{file = "cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c"},
{file = "cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb"},
{file = "cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0"},
{file = "cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4"},
{file = "cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453"},
{file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495"},
{file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5"},
{file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb"},
{file = "cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a"},
{file = "cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739"},
{file = "cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe"},
{file = "cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c"},
{file = "cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92"},
{file = "cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93"},
{file = "cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5"},
{file = "cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664"},
{file = "cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26"},
{file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9"},
{file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414"},
{file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743"},
{file = "cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5"},
{file = "cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5"},
{file = "cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d"},
{file = "cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d"},
{file = "cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c"},
{file = "cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe"},
{file = "cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062"},
{file = "cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e"},
{file = "cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037"},
{file = "cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba"},
{file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94"},
{file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187"},
{file = "cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18"},
{file = "cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5"},
{file = "cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6"},
{file = "cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb"},
{file = "cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca"},
{file = "cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b"},
{file = "cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b"},
{file = "cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2"},
{file = "cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3"},
{file = "cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26"},
{file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c"},
{file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b"},
{file = "cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27"},
{file = "cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75"},
{file = "cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91"},
{file = "cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5"},
{file = "cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13"},
{file = "cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b"},
{file = "cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c"},
{file = "cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef"},
{file = "cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775"},
{file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205"},
{file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1"},
{file = "cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f"},
{file = "cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25"},
{file = "cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad"},
{file = "cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9"},
{file = "cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d"},
{file = "cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c"},
{file = "cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8"},
{file = "cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc"},
{file = "cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592"},
{file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512"},
{file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4"},
{file = "cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e"},
{file = "cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6"},
{file = "cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9"},
{file = "cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"},
{file = "cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7"},
{file = "cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c"},
{file = "cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165"},
{file = "cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534"},
{file = "cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f"},
{file = "cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63"},
{file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2"},
{file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65"},
{file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322"},
{file = "cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a"},
{file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"},
{file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"},
]
[package.dependencies]
pycparser = {version = "*", markers = "implementation_name != \"PyPy\""}
[[package]]
name = "charset-normalizer"
version = "3.4.7"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
optional = true
python-versions = ">=3.7"
groups = ["main"]
markers = "extra == \"docs\""
files = [
{file = "charset_normalizer-3.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d"},
{file = "charset_normalizer-3.4.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8"},
{file = "charset_normalizer-3.4.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790"},
{file = "charset_normalizer-3.4.7-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc"},
{file = "charset_normalizer-3.4.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393"},
{file = "charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153"},
{file = "charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af"},
{file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34"},
{file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1"},
{file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752"},
{file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53"},
{file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616"},
{file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a"},
{file = "charset_normalizer-3.4.7-cp310-cp310-win32.whl", hash = "sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374"},
{file = "charset_normalizer-3.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943"},
{file = "charset_normalizer-3.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008"},
{file = "charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7"},
{file = "charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7"},
{file = "charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e"},
{file = "charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c"},
{file = "charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df"},
{file = "charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265"},
{file = "charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4"},
{file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e"},
{file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38"},
{file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c"},
{file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b"},
{file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c"},
{file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d"},
{file = "charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad"},
{file = "charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00"},
{file = "charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1"},
{file = "charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46"},
{file = "charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2"},
{file = "charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b"},
{file = "charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a"},
{file = "charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116"},
{file = "charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb"},
{file = "charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1"},
{file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15"},
{file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5"},
{file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d"},
{file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7"},
{file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464"},
{file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49"},
{file = "charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c"},
{file = "charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6"},
{file = "charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d"},
{file = "charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063"},
{file = "charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c"},
{file = "charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66"},
{file = "charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18"},
{file = "charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd"},
{file = "charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215"},
{file = "charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859"},
{file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8"},
{file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5"},
{file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832"},
{file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6"},
{file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48"},
{file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a"},
{file = "charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e"},
{file = "charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110"},
{file = "charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b"},
{file = "charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0"},
{file = "charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a"},
{file = "charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b"},
{file = "charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41"},
{file = "charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e"},
{file = "charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae"},
{file = "charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18"},
{file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b"},
{file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356"},
{file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab"},
{file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46"},
{file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44"},
{file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72"},
{file = "charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10"},
{file = "charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f"},
{file = "charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246"},
{file = "charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24"},
{file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79"},
{file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960"},
{file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4"},
{file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e"},
{file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1"},
{file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44"},
{file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e"},
{file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3"},
{file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0"},
{file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e"},
{file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb"},
{file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe"},
{file = "charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0"},
{file = "charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c"},
{file = "charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d"},
{file = "charset_normalizer-3.4.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e5f4d355f0a2b1a31bc3edec6795b46324349c9cb25eed068049e4f472fb4259"},
{file = "charset_normalizer-3.4.7-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:16d971e29578a5e97d7117866d15889a4a07befe0e87e703ed63cd90cb348c01"},
{file = "charset_normalizer-3.4.7-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dca4bbc466a95ba9c0234ef56d7dd9509f63da22274589ebd4ed7f1f4d4c54e3"},
{file = "charset_normalizer-3.4.7-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e80c8378d8f3d83cd3164da1ad2df9e37a666cdde7b1cb2298ed0b558064be30"},
{file = "charset_normalizer-3.4.7-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:36836d6ff945a00b88ba1e4572d721e60b5b8c98c155d465f56ad19d68f23734"},
{file = "charset_normalizer-3.4.7-cp38-cp38-manylinux_2_31_armv7l.whl", hash = "sha256:bd9b23791fe793e4968dba0c447e12f78e425c59fc0e3b97f6450f4781f3ee60"},
{file = "charset_normalizer-3.4.7-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aef65cd602a6d0e0ff6f9930fcb1c8fec60dd2cfcb6facaf4bdb0e5873042db0"},
{file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:82b271f5137d07749f7bf32f70b17ab6eaabedd297e75dce75081a24f76eb545"},
{file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:1efde3cae86c8c273f1eb3b287be7d8499420cf2fe7585c41d370d3e790054a5"},
{file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:c593052c465475e64bbfe5dbd81680f64a67fdc752c56d7a0ae205dc8aeefe0f"},
{file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:af21eb4409a119e365397b2adbaca4c9ccab56543a65d5dbd9f920d6ac29f686"},
{file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:84c018e49c3bf790f9c2771c45e9313a08c2c2a6342b162cd650258b57817706"},
{file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dd915403e231e6b1809fe9b6d9fc55cf8fb5e02765ac625d9cd623342a7905d7"},
{file = "charset_normalizer-3.4.7-cp38-cp38-win32.whl", hash = "sha256:320ade88cfb846b8cd6b4ddf5ee9e80ee0c1f52401f2456b84ae1ae6a1a5f207"},
{file = "charset_normalizer-3.4.7-cp38-cp38-win_amd64.whl", hash = "sha256:1dc8b0ea451d6e69735094606991f32867807881400f808a106ee1d963c46a83"},
{file = "charset_normalizer-3.4.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:177a0ba5f0211d488e295aaf82707237e331c24788d8d76c96c5a41594723217"},
{file = "charset_normalizer-3.4.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e0d51f618228538a3e8f46bd246f87a6cd030565e015803691603f55e12afb5"},
{file = "charset_normalizer-3.4.7-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:14265bfe1f09498b9d8ec91e9ec9fa52775edf90fcbde092b25f4a33d444fea9"},
{file = "charset_normalizer-3.4.7-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:87fad7d9ba98c86bcb41b2dc8dbb326619be2562af1f8ff50776a39e55721c5a"},
{file = "charset_normalizer-3.4.7-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f22dec1690b584cea26fade98b2435c132c1b5f68e39f5a0b7627cd7ae31f1dc"},
{file = "charset_normalizer-3.4.7-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:d61f00a0869d77422d9b2aba989e2d24afa6ffd552af442e0e58de4f35ea6d00"},
{file = "charset_normalizer-3.4.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6370e8686f662e6a3941ee48ed4742317cafbe5707e36406e9df792cdb535776"},
{file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a6c5863edfbe888d9eff9c8b8087354e27618d9da76425c119293f11712a6319"},
{file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ed065083d0898c9d5b4bbec7b026fd755ff7454e6e8b73a67f8c744b13986e24"},
{file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2cd4a60d0e2fb04537162c62bbbb4182f53541fe0ede35cdf270a1c1e723cc42"},
{file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:813c0e0132266c08eb87469a642cb30aaff57c5f426255419572aaeceeaa7bf4"},
{file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:07d9e39b01743c3717745f4c530a6349eadbfa043c7577eef86c502c15df2c67"},
{file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c0f081d69a6e58272819b70288d3221a6ee64b98df852631c80f293514d3b274"},
{file = "charset_normalizer-3.4.7-cp39-cp39-win32.whl", hash = "sha256:8751d2787c9131302398b11e6c8068053dcb55d5a8964e114b6e196cf16cb366"},
{file = "charset_normalizer-3.4.7-cp39-cp39-win_amd64.whl", hash = "sha256:12a6fff75f6bc66711b73a2f0addfc4c8c15a20e805146a02d147a318962c444"},
{file = "charset_normalizer-3.4.7-cp39-cp39-win_arm64.whl", hash = "sha256:bb8cc7534f51d9a017b93e3e85b260924f909601c3df002bcdb58ddb4dc41a5c"},
{file = "charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d"},
{file = "charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5"},
]
[[package]]
name = "click"
version = "8.4.1"
description = "Composable command line interface toolkit"
optional = false
python-versions = ">=3.10"
groups = ["dev"]
files = [
{file = "click-8.4.1-py3-none-any.whl", hash = "sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2"},
{file = "click-8.4.1.tar.gz", hash = "sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96"},
]
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
[[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"
groups = ["main", "dev"]
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
markers = {main = "extra == \"docs\" and sys_platform == \"win32\"", dev = "platform_system == \"Windows\" or sys_platform == \"win32\""}
[[package]]
name = "coverage"
version = "7.14.1"
description = "Code coverage measurement for Python"
optional = false
python-versions = ">=3.10"
groups = ["dev"]
files = [
{file = "coverage-7.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3e3680291c4a1d0dadfa84a2c459576a4af5133abb617905714339a0c73138cf"},
{file = "coverage-7.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a5274669f37f2343635a347b91a60777621341ab3378e9c6ac9335eee704bddf"},
{file = "coverage-7.14.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cfe5a5fec635799ef33428f1e5e61bafa45a92a96190ba731561ba558ccc214d"},
{file = "coverage-7.14.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:62a9f70b52e0b5a95cfef4a5c5641b06983cadc5e538a3feeb5c00211f523ac2"},
{file = "coverage-7.14.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c18ebc343e15be53049b3a2dce38fe82d58f37e20ab9094b3a39c0aa4f6bb47"},
{file = "coverage-7.14.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b84ffdf877644e7096aa936991efeed873f7f3df57b9cd001312b7668ab08550"},
{file = "coverage-7.14.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e854312c4103f2ad4c0dc023b69b77ebfd2c89db5f86c4c94dc2353f9a92167e"},
{file = "coverage-7.14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c643734307300234fafa36bf2a040a7235f8f177ea1fd6ec1423aea6fb7b929f"},
{file = "coverage-7.14.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:84ac9499e48700399a5dd0ea7085b5091961fec52c68d66b4ec0d3cf7f4441b1"},
{file = "coverage-7.14.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:7f02d09f70776579b926d889a4c9c235070a1f47c40458aeaca563fae5acfdb5"},
{file = "coverage-7.14.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:ce66d8e46da2bb5ee313a745cbd2e391d319176c1f7a9451bfcd3a2fb920859b"},
{file = "coverage-7.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c912c259304cfb5ee584481cfb7ce1ff932b4d61e6c9140b8f19cb7b5ed82332"},
{file = "coverage-7.14.1-cp310-cp310-win32.whl", hash = "sha256:1238cb94638e610e972c60dac68e813f868dc7d6e982535270558443058d9d59"},
{file = "coverage-7.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:fc459e5d73be2d6332fcfe8dbf3d8994671fe33c700f4565988ecfa511547253"},
{file = "coverage-7.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:478b5bcd63c2e1357c5c7e16c070690df7b07f676b1c114d7b93e533c664309f"},
{file = "coverage-7.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a24a81f9715ee42ef59a316cc11611c98fe23920f7c81861315c9f3ff4a230f4"},
{file = "coverage-7.14.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:196a13319ad88d6d8ef5ab489ec4f44ddde2143c0c7d5b27786f6c3ffd56a7e1"},
{file = "coverage-7.14.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3d452fd08b5c72c5167c93e6867b5c08500bd40f2a21e1e854a500550b6cc36f"},
{file = "coverage-7.14.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23bf7fa51ac02e07fc7c96849b82946da47ae862dc8f86d183b2a4864fc38129"},
{file = "coverage-7.14.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bcaa50684dcaadfa599ac48f81103c756d791cfd85c97203d2217c593d48b860"},
{file = "coverage-7.14.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4ea1c034f95c9b056e856b794630b17f9fa3d57e4800ff1e503d3be0f9c9078c"},
{file = "coverage-7.14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c7e057326434e441306226fbeb5d1aaf14a2637efe97ba668306635835f32ad7"},
{file = "coverage-7.14.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:59baf88468dbc8d63b1887afd92bda52e40bb1561696e5819670601403810cec"},
{file = "coverage-7.14.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d34d75f892b3ab73ba11cab5442cce7b3e168fd64162b16f0e1e0d09c508edef"},
{file = "coverage-7.14.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3a56abc20a472baf0304c455721bc601477440d28ecfde8a03dde79ede07e0df"},
{file = "coverage-7.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6a3cb83d1552c0cd1b4906655b6a33fd4a8473229633a901c6b73bf86914dee9"},
{file = "coverage-7.14.1-cp311-cp311-win32.whl", hash = "sha256:10274a1fbeb8ec5d72966e17bb198a3104257aca4ac09d98667c5f8aca8c8548"},
{file = "coverage-7.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:87ebdf787d4888e3f3f2d523eadc6e18c6d18c6d0eb173801a189641627fb37e"},
{file = "coverage-7.14.1-cp311-cp311-win_arm64.whl", hash = "sha256:dd34767fa19848d35659ffc0a75314f58c7af3f1cd87ec521e8292a1238398a3"},
{file = "coverage-7.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a06c76364a9360e33d6d23769aefdf7f66f38e2ffb60ceb1baaa4989d83b695c"},
{file = "coverage-7.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fad54e871165f6ec2f536063ac74c3104508a12963e64072ba44bd822de52b0c"},
{file = "coverage-7.14.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:84b535f00655ecafe1d929d1fb00ed5d6fa3051ea643ab2c161a3887b86f294b"},
{file = "coverage-7.14.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6b6b0853b895fe0e98cbfc580d1ec3393d9302b4b1e96a77b3f5c91fdab899e6"},
{file = "coverage-7.14.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:442cc9c952b2df400cda54bb04ab87330cf2cd08a8692cbbea36773531eb6f37"},
{file = "coverage-7.14.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8270544c361ed405a27a060dbc9ed2c124b084d96dfdc2d9a2510482aef981ad"},
{file = "coverage-7.14.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:48b283b1dd6372e8de2a7a9a4c4d5dc06f4d4fd209b876f3c88a7a205a0c8f84"},
{file = "coverage-7.14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5b0c99ba93a07d56f6df340bb79be53202a082b2fdb81bfe6190b741a3470d54"},
{file = "coverage-7.14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e471bc5769ff073b058cfadb0d736b56ce067c8560eabeb0da88462df98c23e7"},
{file = "coverage-7.14.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f497a1ea81d4cd7c10ddcaa685135b9aabd291af3d55775a9ddf3cb7a364cdd9"},
{file = "coverage-7.14.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:2222be86d0b54f5dd5a38f45f17f315f737245e857bf0bdedc70734f84a13c02"},
{file = "coverage-7.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:85e85586565842f6932abebd4c18bcb1074223dc0b3576e7d173ca710622813a"},
{file = "coverage-7.14.1-cp312-cp312-win32.whl", hash = "sha256:4a28fd227808366b196a75476dced2eb35b351d6766ba9c858dc93319e87f4f1"},
{file = "coverage-7.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:54acdb6674a4661768d7bf7db32dfb9f46ab1d764f8aba6df75ce1a6a088724e"},
{file = "coverage-7.14.1-cp312-cp312-win_arm64.whl", hash = "sha256:99cd41ff91afd94896fea3bc002706b6ae4ce95727d06e4a0f39c0a8d8bd8b1a"},
{file = "coverage-7.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:be9f2c802dcfce3f71298303aa5dad0dce440a76c52f2f60dacd8656dab78793"},
{file = "coverage-7.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6223a72fd0e4c7156353ec0f08a5f93623e1d3034d0e2683b9bb8ea674131b1d"},
{file = "coverage-7.14.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7279d2110a28cebc738b6459ecda2771735a4c18465fbbd36b3288fe5ed92247"},
{file = "coverage-7.14.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9eeb3fcbc13ba40dfbdb22d01d196a28e9cef9ed4c29b60061a1e0e823a9929d"},
{file = "coverage-7.14.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f0cfc27c539f07cf5c0a4cfe211d0b6cae039f8f40526dbaa71944e64b50a7b"},
{file = "coverage-7.14.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:221c70f316241a78e77e607c227cefc8808d4e08f28d99c04f35694690e940be"},
{file = "coverage-7.14.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:da028256b04ec30e5e0114b6f76172938c313991f0a2d3d894271315cf5d5e43"},
{file = "coverage-7.14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76a085d7005236a767e3426148b2c407e53ad61695c562f8a81da2d373324901"},
{file = "coverage-7.14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b553d04b5e778a8e56d57eb134aff42a92718ecba45e79c4764ecfa40efd92ff"},
{file = "coverage-7.14.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:46f714d2fb8ae2f4f29f23ada7f1e79b759fff5a70f94a1dac23af204c3ec9e4"},
{file = "coverage-7.14.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:1896f5e19ff3f0431c7ce2172adc54890fd97f86b59ced8ca1649145d9ffe35d"},
{file = "coverage-7.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:62fd185ef9df3c33d1c8178c5af105f762afbad96038de9a4ae100aa6297ca33"},
{file = "coverage-7.14.1-cp313-cp313-win32.whl", hash = "sha256:ab4af6352741a604c431c6072fce5bee33bf0f20dc7a56618d6bf6bb89e9810c"},
{file = "coverage-7.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:7af486dabe8954d03b087f0021540897afe084f04e16ff5579e08cc46f871416"},
{file = "coverage-7.14.1-cp313-cp313-win_arm64.whl", hash = "sha256:2224f89ffd0c5605ccce1ed7a584da162bc7c55f601ab1c946bc9de31a486b42"},
{file = "coverage-7.14.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de286598cc65d2b489411174b1faec2f5a7775fb3201fd925db2a76b4030f37d"},
{file = "coverage-7.14.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:042c46ded7c288aeb07cf14a28b6c1e10b78fcba40171c3fa1e939377eeef0b5"},
{file = "coverage-7.14.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f4ddbe407477f04c45115d1a4e5bc480f753553b534d338d4c3358b1cdd0ea52"},
{file = "coverage-7.14.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d13e6725992e2d2fd7d81d4f5241952d13740121dfd501da09201be39b2c003a"},
{file = "coverage-7.14.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f747dc8edcfe740130f28f32f3995e955494285717e86ee25af51db2219df08a"},
{file = "coverage-7.14.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ced2f09ef276fd58611a1ef502164ad266d2b75174e5a40cabbdb4033f9f6cf2"},
{file = "coverage-7.14.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b84800013769a78ccb9ef4659402e26d06867e337b61ec365f77ad008adea80e"},
{file = "coverage-7.14.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ea8cd6ca0ee9f616aaef3afc6882e32c2cbf18b00d96313ffd76af650574034d"},
{file = "coverage-7.14.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:aa5e304a873fabddc11e484e9b6b738bd38bd7bed17b09aa84eecf5332e8b8bb"},
{file = "coverage-7.14.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5a1c5215be81035e629d5bc756650634d0bf31991038db7a0eccb90f025ce16d"},
{file = "coverage-7.14.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:79058c47dae6788504b5effb319961bcd72d7240551464b91d474bc0ed186d69"},
{file = "coverage-7.14.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:370c5afae3fa0658e11694a32b24c2778f6bc2d17718121f94ee185e69f26b54"},
{file = "coverage-7.14.1-cp313-cp313t-win32.whl", hash = "sha256:3758dd0a7f1fa57365ef2e781df0f0731d38b6e3772259d13dae4bd8a958d4b1"},
{file = "coverage-7.14.1-cp313-cp313t-win_amd64.whl", hash = "sha256:6ff665fb023a77386fe11685190cee1f60a7d635994a30d9b0a061533d470fce"},
{file = "coverage-7.14.1-cp313-cp313t-win_arm64.whl", hash = "sha256:17a5a241e5997621a956a7f402a7433ef4221e5152809b785bec79e2323799f1"},
{file = "coverage-7.14.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d5ed429d0b8edaac649e889b4ffcedb6c80b06629a3f93050e3dddfb99235bee"},
{file = "coverage-7.14.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8011224a62280e50dab346960c03cf47aca1a1e09e608c0fb33fd6e0cc8e9500"},
{file = "coverage-7.14.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:12c42ec1e14f553c4f817e989365982e646e27211f10a0f717855b94a79c8906"},
{file = "coverage-7.14.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:06144cd511cf2624873a035c5069cf297144f6e77a73ee3d7a55b605ec5efb42"},
{file = "coverage-7.14.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a311d8e1da24be5c1ccf85cbfb06315dbaa1703d5a1eab3f6432c72b837917c8"},
{file = "coverage-7.14.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c79cead5b5bc584d9c71451cb984d0e3a84e0c0937379c8efcbf27c8d661b851"},
{file = "coverage-7.14.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dcbf65f1f66a26cdd88c35cf68fb4729c5d1cd2e88added72420541dfb212034"},
{file = "coverage-7.14.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fd86572566fb40189a8260446158235159bc7a82dfbc87a3b39cf4fb57fcec1c"},
{file = "coverage-7.14.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:7771b601718fdde84832c3a434ca9bbf4ae9adbc49d84198b4110700c3c77c36"},
{file = "coverage-7.14.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:39b21e212c55af06fa375e3dbf90a8a8e38792f3a910c580066d23563830ddd5"},
{file = "coverage-7.14.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f2302660e32562a532b442480121aef8aa61a5bdb20b30bf0adab29f10a5a4b4"},
{file = "coverage-7.14.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:03a6f93c1ec3b7f2e77b5dbcc5573a2c21f12529a5c6bbe0f16f72303cc2fa4d"},
{file = "coverage-7.14.1-cp314-cp314-win32.whl", hash = "sha256:8a3ce026d73290f42f08dafecbd82c193a74df280461fbf97300fec51fd133ee"},
{file = "coverage-7.14.1-cp314-cp314-win_amd64.whl", hash = "sha256:114c95ef29302423b87d159075805f4ab973254a2638a5d7d046c94887cc87d7"},
{file = "coverage-7.14.1-cp314-cp314-win_arm64.whl", hash = "sha256:a07891c3f4805442b31b71e84ba3cf29ed1aa9a428284e06deeb4b23e5b46343"},
{file = "coverage-7.14.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1101a5ebb083aecb625ebb6209d4105b58f647b093cb2dc8122d7b33f743cfe1"},
{file = "coverage-7.14.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:851b9e1e4e8a4608e77c79714b2e77c0970d2ed7202a05e92ae407817481887b"},
{file = "coverage-7.14.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d5b89cdfb2ee051b71e8c3c70bd81a9eff81100f736a269136fe1a68efe00474"},
{file = "coverage-7.14.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0177614a0370f227888b4e436a7c55686d6a9f90eb1ade2b624ba685a1686e86"},
{file = "coverage-7.14.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2d69af5dea2de76fc485a83032a630523f985198b7e25be901ec60181587b01e"},
{file = "coverage-7.14.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:35ab22d91de736e8966b980dc355cbcdd2c6dbbcfe275f9a2991bc8a91b3df65"},
{file = "coverage-7.14.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:357d4e32935c36588aaba057d734fa32428c360c9fc2e4442afbf1b646beee6e"},
{file = "coverage-7.14.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:51bd64741cc6fa065abd300ede1afe5a5291ece9c31da8b24884deda48bcc3f8"},
{file = "coverage-7.14.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:9132cd363a68a4c3daa7c8704a654b1e39d3360f6f5b8ddd470608a945236c07"},
{file = "coverage-7.14.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:07c6290b1697b862c0478eab545eec949a0d0e4d6d03497f446d706da3b4f2de"},
{file = "coverage-7.14.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5ea0c297e27133853b4d8a3eb799bff5a2dbd9f2f41537a240d337ac9b4df890"},
{file = "coverage-7.14.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:01b7733daad0237daa01ef80fe2dfceffc911e6a17fa7b55d14aa8214eaaaecd"},
{file = "coverage-7.14.1-cp314-cp314t-win32.whl", hash = "sha256:6adc5a36984624a70bf11d7184e20fa0a49aa7c47ffab43804106a1a695ea22e"},
{file = "coverage-7.14.1-cp314-cp314t-win_amd64.whl", hash = "sha256:ddf799247318f34dbcd2efa8c95a8d0642674e926bb1774cf9b63dfd2a389d1c"},
{file = "coverage-7.14.1-cp314-cp314t-win_arm64.whl", hash = "sha256:145986fe66647eb489f18d9a997567a3fd358584c4b5a808769113abc07466af"},
{file = "coverage-7.14.1-py3-none-any.whl", hash = "sha256:a252f21c27e38347e60111a3266b03827422a7d5525951aceee313aa68bab1d2"},
{file = "coverage-7.14.1.tar.gz", hash = "sha256:30c08f7d90415aa98b3c990385dea2939b0da55f38515e5b369b83655f8523be"},
]
[package.extras]
toml = ["tomli ; python_full_version <= \"3.11.0a6\""]
[[package]]
name = "cryptography"
version = "49.0.0"
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
optional = false
python-versions = "!=3.9.0,!=3.9.1,>=3.9"
groups = ["main"]
files = [
{file = "cryptography-49.0.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:966fe0e9c67490071f14c0d2b1cb2dfb3023c5ce39457343931415f08382f2db"},
{file = "cryptography-49.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:36d1709f992593689b45bda411498d62c6e365f2ca00b84657d4dadd24de16db"},
{file = "cryptography-49.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0e959b578856a3924bc0cbb710fc12c387b9412a951389f3ca61704a9e25f325"},
{file = "cryptography-49.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:53ecee2e23f7169b6117e99fc8a944e5e50f79e69758a83b52a00cb98ab2b2d2"},
{file = "cryptography-49.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:2eda353d8a27bcbcaa4cbed18994a74ab4d19a2ca897db188ea269ab9b71419b"},
{file = "cryptography-49.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2afe9051da7ae7bd5905da5a949280c7d2bb75682e188f650a9d0f2756b834c6"},
{file = "cryptography-49.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:0b82e28ee398a386f0807bba7884d30f25218855690f45115831bcce5d90822c"},
{file = "cryptography-49.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:ccac2bfebc306b862133e3bb71f3f6ee8bb525240089b2d952e4144b3a6d5da7"},
{file = "cryptography-49.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d0527ce944105f257f605a827d6ebead966c752038b6e8656abb9c5edee6fc68"},
{file = "cryptography-49.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:cbc77da8c523d5abd028635ba850a6966fcee2c82e2bf65a41d1d8afe0f98be9"},
{file = "cryptography-49.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b87e65d263b3e5d3bb92a57e2a6638e2f31110fa7aa890c7b2dbba42248d0a3f"},
{file = "cryptography-49.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:66ec79c3904820572d7e987abdf304281f141d37ad9a489b8e97066e7b9b6459"},
{file = "cryptography-49.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:e5dfc1e64de5677cec922ffa8da89c546d0415bf6efdf081842e5d44c84e1f0e"},
{file = "cryptography-49.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:73a205dce83953d131a4aa1e0fd917a2fd1c5b1eef251e9d7152efefcbf5caf7"},
{file = "cryptography-49.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:196ecd6a36e4e9aa10270393bb98d8df88fccee0bf1e5128b91ae4eb4375896d"},
{file = "cryptography-49.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7abcee80084cda3f7691f3eb1ce480d8df49cec637b429aa35986c1de71738aa"},
{file = "cryptography-49.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:4ae387c9cb68ea569ca17e490d66d8142b81c3cc814bf179974b7d146e490bbb"},
{file = "cryptography-49.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:f37d847238971164fdbc68ade6f6574aecc9c0af714190e2083429ff68f4ce9d"},
{file = "cryptography-49.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:c2bc30226390d60ea19d9f82b19db005fe0452154a23c1c410c12ea801e43561"},
{file = "cryptography-49.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:07cab27cc7b7e0fd28e5e26bb9eeedde5c135c868b46de4a27845abe94af6122"},
{file = "cryptography-49.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:b20133d204d2bb56ba047642199603876c872026ca53e79c35b83772ab2cc505"},
{file = "cryptography-49.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b970c6da94d5bb18629db453d14f2a1300f6bf59b61e9b82377931ef95504866"},
{file = "cryptography-49.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:d8ecde755e2e91bf773fc94e8c9d730cd7f2007004cb492263a794ec3899a1c8"},
{file = "cryptography-49.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e3fb64c420688e5319ae25113a354015abbd8dffbfbc41781a1ea66fc7622ac3"},
{file = "cryptography-49.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32703d93296f5c1f4b53349ad3a250c2cae0fdecd3a3dd5d47e616d8d616af27"},
{file = "cryptography-49.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:33cd0565932807baddb67b96dbee92f2c374b5c89dee09fd74079aeb8c8dba61"},
{file = "cryptography-49.0.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ec5e529fb80935c94fe7b729f9972b50e351a0e6b50aa294fd5cabb109fcc29a"},
{file = "cryptography-49.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f78ff2c9ed8dc2d036b0f4d640e22522213d047c1b14e61205a7e55c80a494d4"},
{file = "cryptography-49.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:35b151772baff2c74cba7fa290ceaff4c3b11c0c881eb93eb5dbc05a7cfbba18"},
{file = "cryptography-49.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0f21641cf4b30fca7aee061ced0ec7ad7b073518088b7c9969a297c0ae796c69"},
{file = "cryptography-49.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:9e82dcc8e56052715fb18b2429e3bca4823b1629136a2084fc45a9a5cecb9b64"},
{file = "cryptography-49.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6f2debedf9ca60cf1d5bd466475638af5130f89965605cd818484d19987d3a21"},
{file = "cryptography-49.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:8c25ceb16df5b9435f3f6a9829204985b0e0cbee3b48aacd432c7d2c850b44d9"},
{file = "cryptography-49.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:28d8b15e6275f12c8a207dc309dfa957903c927d08d0cc937ee3f63f200693cc"},
{file = "cryptography-49.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:6fc361c34fb6aac015ce19435876635e5c6d21db31998b0920f675f131e043b8"},
{file = "cryptography-49.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:2400ef9c9e2299a25614eb1dea3db54a69b1349efd043bfac9c67630d136df36"},
{file = "cryptography-49.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:67e1d20ad9ef3a563c59ef22e7a8a0b8210bd26604369ea4a30a7c66aefe504e"},
{file = "cryptography-49.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:42b0684e0e40cf26122427802486f6d93aea593612603a94fbf260c7eb1e9c1b"},
{file = "cryptography-49.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:026ac7423e6fa66872d3bf889be5974507da3944f866f704fa200eadacd00001"},
{file = "cryptography-49.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:fc1e275c2f1d97b1a6450b8b0ea3ebfa6e087a611c2b26cb2404d48588abab7b"},
{file = "cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c83782480a4a9da4d0feb51950131ba32e12e70813848b3343f6e18c28a66838"},
{file = "cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b39efa323140595abd3ecca8529d321ae50f55f3aa3ba9cc81ea56a6011953d5"},
{file = "cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:b47db11c2c3525083296069b98ac5221907455e989ae0c2e3008bde851921615"},
{file = "cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:084ef1af862eb07ec46d25f68689f2102a9fc0e05ce7b80f14f5fe51e4eef0f6"},
{file = "cryptography-49.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:be9fcb48a55f023493482827d4f459bd263cc20efde64f204b97c123201850c6"},
{file = "cryptography-49.0.0.tar.gz", hash = "sha256:f89660a348f4f78a92366240a61404e337586ef7f5909a2fef59ca88ef505493"},
]
[package.dependencies]
cffi = {version = ">=2.0.0", markers = "platform_python_implementation != \"PyPy\""}
[package.extras]
ssh = ["bcrypt (>=3.1.5)"]
[[package]]
name = "dbus-fast"
version = "5.0.22"
description = "A faster version of dbus-next"
optional = false
python-versions = ">=3.10"
groups = ["main"]
markers = "sys_platform == \"linux\" or platform_system == \"Linux\""
files = [
{file = "dbus_fast-5.0.22-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:027b358d55164f722d7432d6734506751c89b5cee8d516e7282eff3eaa763f05"},
{file = "dbus_fast-5.0.22-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f8244561e503cffcd5e6b1ff6544a44aa0b65b6856f2f64d04345ebf82f8ac48"},
{file = "dbus_fast-5.0.22-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c32869678240f233ece5a1137960c0337699dbb39acc02c155929f52543f03fb"},
{file = "dbus_fast-5.0.22-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f74700b1b4fd58acb76f8790e2cd475084d0e59149e0cc035abd239435a339b7"},
{file = "dbus_fast-5.0.22-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:246780dcda59d26dd2fb1d9a15991bc8483e9c241cee8713920a39a363ad4d39"},
{file = "dbus_fast-5.0.22-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:58e4d41f9fea22cfd6dacdf03d53b932bbe15a825af5abad3ca79b91493a15c3"},
{file = "dbus_fast-5.0.22-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d8ca39f3f2ab34f88bb343db87c7cf8b07eefa046b23d8c38b3387f150ac609e"},
{file = "dbus_fast-5.0.22-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f0926e4cf49989b4ec8233e8fd462eb35a640fcfe81bb75d91675dd47489022b"},
{file = "dbus_fast-5.0.22-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:846f9a6602b4383f989201f7851459fb225a8912cd24b38e63894748545c3040"},
{file = "dbus_fast-5.0.22-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:886b43446b6fdc3986befbbb88db1365b14e49dd0a7edf84c2c67ac66c7160a4"},
{file = "dbus_fast-5.0.22-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3a699fca957acc845ddb12b47f741dba23ce147fdb93583e0c7e7bad3e9b2355"},
{file = "dbus_fast-5.0.22-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9a5b05fd4973862042e5bee2c5e8c5a15297e0b33a975bf25b44becf7bcb3618"},
{file = "dbus_fast-5.0.22-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:6c4dae5292a7924ec062815c34b49043d8386cd22e165f9fb4012de00997cdf1"},
{file = "dbus_fast-5.0.22-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b7d90a52be79acbaef257f3a81d5b9b9dec40f1bad29429ac5c7802684fb9b84"},
{file = "dbus_fast-5.0.22-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f72b77be63f7bb24cf42936ad10994d40f43fed691f857f7854b5882d6a5227c"},
{file = "dbus_fast-5.0.22-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f0bcad7f71d2304a68a5b0bc0d24c3fcc14710a2ffcf5f2a27521e3aece71ca"},
{file = "dbus_fast-5.0.22-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4ffcf16034f71a801bd2108aeffb6337d104c9459e8b1a218d16a917c8a2d2e9"},
{file = "dbus_fast-5.0.22-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:98de6d2c200d8182e1fd0bdde3206fa556b8fa14ebb752a044cd8daa87b4658c"},
{file = "dbus_fast-5.0.22-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b013437b66dc22b8d9aca5e0b0d46bf1980208a143409469fe482d9684a2a717"},
{file = "dbus_fast-5.0.22-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:855f15b7f7805171da2b82de1c317d01cfbb9fb8ac61fcc1e8dec54d8c69fab7"},
{file = "dbus_fast-5.0.22-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7d1c42963235cfc015a2d2b8c5fe42b65387493b4ad4ce0ec122601c805e6742"},
{file = "dbus_fast-5.0.22-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:26e26b409e1e6edf5e2a4df8d192625fb38876b074fb5c7d0a5b15c5792e549d"},
{file = "dbus_fast-5.0.22-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de10ff3b3cb2acb1c09fe17158a470519000d37bb5ee5fd69c4075e81ce8dcf5"},
{file = "dbus_fast-5.0.22-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:979761985fe343c701f2b7575285d6e370123f7231d4656209ef7824bb686bbb"},
{file = "dbus_fast-5.0.22-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fb73f1d8374253b7c17d69e902cf2ded1bfb089cb6ae67c10b4e0bdfe1b8fe08"},
{file = "dbus_fast-5.0.22-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b67a02037eb58bcf9e445df60ea0d9d7346fd334abde3aa62e03c75823b53979"},
{file = "dbus_fast-5.0.22-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:83940ea00d7ee2f0c5bcb5d19d7d05e7949e52d467616a0b735d72e7285402ec"},
{file = "dbus_fast-5.0.22-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:279d212e9fb262d595af2e4b5b9e951bc00c73a5c8eeb50f158caa13705b9c84"},
{file = "dbus_fast-5.0.22-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:511655d915692f55b8c5f5a535acf80cca9c6d1a35384db7fcfdabaae05dd837"},
{file = "dbus_fast-5.0.22-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:703e0f8f9af52e8e053394ee2b578042be0c3d8ea2b1488f9db8cb14393cc13f"},
{file = "dbus_fast-5.0.22-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb1d7e8e65561d0fd438004fd9e0f981c8a862912fed58dd4e29db1936c39d73"},
{file = "dbus_fast-5.0.22-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:959fab6420897ab99410e67d6f9f9a7f6f4cedb6014700768f5e2d71dbff5dc6"},
{file = "dbus_fast-5.0.22-cp314-cp314-manylinux_2_41_x86_64.whl", hash = "sha256:eb31c5ff339a7071b914617a69d5b7c6ba7d411da4b01a5f9b5b2fe51e9d1301"},
{file = "dbus_fast-5.0.22-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:856f0543c593f3480e93e67bcd1aa4ddc1d94a6076cfd3ad4e0f5e2b01b33dc3"},
{file = "dbus_fast-5.0.22-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:96d231d128c1f46f263790335897195dde9dac2f38571782db8ae1d8647bd548"},
{file = "dbus_fast-5.0.22-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:595bd3ccfd8318cbafff79f33a15709fee3728724fd61d5fa220080d73b574cb"},
{file = "dbus_fast-5.0.22-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:69a077c296eaab8c30160e861b5514c33d99d67d41d17dbf02e89aae44543b11"},
{file = "dbus_fast-5.0.22-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:04bac97d0cb754a4d13037d0132517f1df28192d6e0568a0bf6df06623062285"},
{file = "dbus_fast-5.0.22-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3eb57d592d84b0bb90e0c077db7ecb61562f49cc9b86a3ef08cbe17243e9cc4f"},
{file = "dbus_fast-5.0.22-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:de4d235d1282ebb3ab65b6cddab84e914c045d92ceb381ddcbdbaf66bf1fb132"},
{file = "dbus_fast-5.0.22-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:048f34299fbe82d7b87c56f47e8bd83f62339a4517685abc6671d603a55d2c89"},
{file = "dbus_fast-5.0.22-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:92df9fb6d8adeb17b534621c2ee730295bbe1d0c2584d5c82b1db478e3f04e8f"},
{file = "dbus_fast-5.0.22-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7be4271e38251f1ad726962dec60da887c8ed352d157352e4fc27f56aece5c5d"},
{file = "dbus_fast-5.0.22-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b3db6388b80ca167299f2eb8eb60e74f07c6e33a1bc4320c42bd9e5ab192c587"},
{file = "dbus_fast-5.0.22-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a127ff97079fd052d2b5dd752abc3fc5678b0d15ce9d04be1796bcc7f90b8b03"},
{file = "dbus_fast-5.0.22-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7af3e81287163dbae29d583a19fdab717a896bb097946adde415439b0ee322fb"},
{file = "dbus_fast-5.0.22-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a160cb931f22c43ddc8efe6ff33f03d3111442b9c0007ba58269a717296d8536"},
{file = "dbus_fast-5.0.22-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7f2af2fc27f3f188387befad5901953863fc8fa6451202083c2b9ec9f970028f"},
{file = "dbus_fast-5.0.22.tar.gz", hash = "sha256:34dc67d7d21a12399828dd13e63b352750580beea54ea7c729e708f2d2905fef"},
]
[[package]]
name = "docutils"
version = "0.22.4"
description = "Docutils -- Python Documentation Utilities"
optional = true
python-versions = ">=3.9"
groups = ["main"]
markers = "extra == \"docs\""
files = [
{file = "docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de"},
{file = "docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968"},
]
[[package]]
name = "flake8"
version = "7.3.0"
description = "the modular source code checker: pep8 pyflakes and co"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "flake8-7.3.0-py2.py3-none-any.whl", hash = "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e"},
{file = "flake8-7.3.0.tar.gz", hash = "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872"},
]
[package.dependencies]
mccabe = ">=0.7.0,<0.8.0"
pycodestyle = ">=2.14.0,<2.15.0"
pyflakes = ">=3.4.0,<3.5.0"
[[package]]
name = "frozenlist"
version = "1.8.0"
description = "A list-like structure which implements collections.abc.MutableSequence"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011"},
{file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565"},
{file = "frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad"},
{file = "frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2"},
{file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186"},
{file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e"},
{file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450"},
{file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef"},
{file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4"},
{file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff"},
{file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c"},
{file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f"},
{file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7"},
{file = "frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a"},
{file = "frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6"},
{file = "frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e"},
{file = "frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84"},
{file = "frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9"},
{file = "frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93"},
{file = "frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f"},
{file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695"},
{file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52"},
{file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581"},
{file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567"},
{file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b"},
{file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92"},
{file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d"},
{file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd"},
{file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967"},
{file = "frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25"},
{file = "frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b"},
{file = "frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a"},
{file = "frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1"},
{file = "frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b"},
{file = "frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4"},
{file = "frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383"},
{file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4"},
{file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8"},
{file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b"},
{file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52"},
{file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29"},
{file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3"},
{file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143"},
{file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608"},
{file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa"},
{file = "frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf"},
{file = "frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746"},
{file = "frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd"},
{file = "frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a"},
{file = "frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7"},
{file = "frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40"},
{file = "frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027"},
{file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822"},
{file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121"},
{file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5"},
{file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e"},
{file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11"},
{file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1"},
{file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1"},
{file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8"},
{file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed"},
{file = "frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496"},
{file = "frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231"},
{file = "frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62"},
{file = "frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94"},
{file = "frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c"},
{file = "frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52"},
{file = "frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51"},
{file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65"},
{file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82"},
{file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714"},
{file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d"},
{file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506"},
{file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51"},
{file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e"},
{file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0"},
{file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41"},
{file = "frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b"},
{file = "frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888"},
{file = "frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042"},
{file = "frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0"},
{file = "frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f"},
{file = "frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c"},
{file = "frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2"},
{file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8"},
{file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686"},
{file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e"},
{file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a"},
{file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128"},
{file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f"},
{file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7"},
{file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30"},
{file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7"},
{file = "frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806"},
{file = "frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0"},
{file = "frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b"},
{file = "frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d"},
{file = "frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed"},
{file = "frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930"},
{file = "frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c"},
{file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24"},
{file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37"},
{file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a"},
{file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2"},
{file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef"},
{file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe"},
{file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8"},
{file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a"},
{file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e"},
{file = "frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df"},
{file = "frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd"},
{file = "frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79"},
{file = "frozenlist-1.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d8b7138e5cd0647e4523d6685b0eac5d4be9a184ae9634492f25c6eb38c12a47"},
{file = "frozenlist-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a6483e309ca809f1efd154b4d37dc6d9f61037d6c6a81c2dc7a15cb22c8c5dca"},
{file = "frozenlist-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b9290cf81e95e93fdf90548ce9d3c1211cf574b8e3f4b3b7cb0537cf2227068"},
{file = "frozenlist-1.8.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:59a6a5876ca59d1b63af8cd5e7ffffb024c3dc1e9cf9301b21a2e76286505c95"},
{file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6dc4126390929823e2d2d9dc79ab4046ed74680360fc5f38b585c12c66cdf459"},
{file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:332db6b2563333c5671fecacd085141b5800cb866be16d5e3eb15a2086476675"},
{file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ff15928d62a0b80bb875655c39bf517938c7d589554cbd2669be42d97c2cb61"},
{file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7bf6cdf8e07c8151fba6fe85735441240ec7f619f935a5205953d58009aef8c6"},
{file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:48e6d3f4ec5c7273dfe83ff27c91083c6c9065af655dc2684d2c200c94308bb5"},
{file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:1a7607e17ad33361677adcd1443edf6f5da0ce5e5377b798fba20fae194825f3"},
{file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3a935c3a4e89c733303a2d5a7c257ea44af3a56c8202df486b7f5de40f37e1"},
{file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:940d4a017dbfed9daf46a3b086e1d2167e7012ee297fef9e1c545c4d022f5178"},
{file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b9be22a69a014bc47e78072d0ecae716f5eb56c15238acca0f43d6eb8e4a5bda"},
{file = "frozenlist-1.8.0-cp39-cp39-win32.whl", hash = "sha256:1aa77cb5697069af47472e39612976ed05343ff2e84a3dcf15437b232cbfd087"},
{file = "frozenlist-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:7398c222d1d405e796970320036b1b563892b65809d9e5261487bb2c7f7b5c6a"},
{file = "frozenlist-1.8.0-cp39-cp39-win_arm64.whl", hash = "sha256:b4f3b365f31c6cd4af24545ca0a244a53688cad8834e32f56831c4923b50a103"},
{file = "frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d"},
{file = "frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad"},
]
[[package]]
name = "habluetooth"
version = "6.8.3"
description = "High availability Bluetooth"
optional = false
python-versions = ">=3.11"
groups = ["main"]
files = [
{file = "habluetooth-6.8.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:badd321cdaab478ac1105bc8b26f3978288c8679af16ac92947a7ee3cb7e0d5a"},
{file = "habluetooth-6.8.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:46bf25a02d4e74decd9203fc8ad15dbbd5211d96b4ac47283c3b087b8ef7a0b2"},
{file = "habluetooth-6.8.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6f95ac3fde60c9cb1ea147661984d497d847cb21465fc2cfb7f6abea483d9ea6"},
{file = "habluetooth-6.8.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6ee7f6229d4e67f9f08e5e67a4600537798a96e226e17a3142a3707b4f5657cb"},
{file = "habluetooth-6.8.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ce4f14e0f093b8e8ba84f336534aed79c5e24f302d38190a12f1d96c0300746a"},
{file = "habluetooth-6.8.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:14133a51c5684974b9f156f4d7905ce13921ab00282dab74e3a539286bd48d96"},
{file = "habluetooth-6.8.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ffc272bc8d27c035255ec733ae1f994823679db57fc8d05038f743c948745101"},
{file = "habluetooth-6.8.3-cp311-cp311-win32.whl", hash = "sha256:5c2f2dbe811c0e7d8e1ee26a6d7adddc002cb62e0a15e90395cc4f4d140579dd"},
{file = "habluetooth-6.8.3-cp311-cp311-win_amd64.whl", hash = "sha256:97dc1c1789142e5f1aeb03e9edb763ef8f321fb7b3a560d74094ca736826bd8d"},
{file = "habluetooth-6.8.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58f32acf524e5477328e395a038d9461027804e197e9f2a64b49ebb48375b634"},
{file = "habluetooth-6.8.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78c89cf66586b806397e55489651d43e5bdc03436e6618d4f9e0e18db814bf72"},
{file = "habluetooth-6.8.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53e29d10f6bf13e37dc26fd39a1d3b392fb85df22cb3efd54b7d40af6c4b1901"},
{file = "habluetooth-6.8.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8e7a333e8a2aaa16c1e39a302ed4fd6cd15f2f5c698c5a78556de981e8c12bb"},
{file = "habluetooth-6.8.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:02b8dfce36ec13139da12da3dde3b42f3e99bf4476c184d6678fe642733f2815"},
{file = "habluetooth-6.8.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:310019e686f892650a4f21cce52bbf0517619eda7f1d4c9be5b9a67282d978c5"},
{file = "habluetooth-6.8.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b449e14401b70bd3f5aaf19fcb8cd2f9fa59d8aafd09baf57c70cfe644019d36"},
{file = "habluetooth-6.8.3-cp312-cp312-win32.whl", hash = "sha256:4523a265d911946673bf513ca38dd98ffcab006b8fdc763bac7068d5578d9afc"},
{file = "habluetooth-6.8.3-cp312-cp312-win_amd64.whl", hash = "sha256:3881afed89b5ea1c87d766d150f3dbe4165415e3f4093a985ccb3f0117298054"},
{file = "habluetooth-6.8.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:589df7d94d30814dfa53f9f5797fc37e281d546fe77ac99bfadc4dc4f140b1d9"},
{file = "habluetooth-6.8.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7bd3dee4684c5b7095d60a4023916f71e122878e4c72d372ead4b7c3a3258ed"},
{file = "habluetooth-6.8.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1b078df6ee2d9e59d0ef1ee219ac4f8037b2d6c4a3ad6f2e37577f088ec0a153"},
{file = "habluetooth-6.8.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc084df3af41be07f34d230f4950980ed774c0ab85919bf20eb7d30547fdcdc1"},
{file = "habluetooth-6.8.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4737893d057246a1ee9e54d394622ebb0af2f47c2deead04bb4e3e9d6411140e"},
{file = "habluetooth-6.8.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:7424030939d79ca504838bca81c9eed2b49de9135a1418c4e0a84232e9aaa730"},
{file = "habluetooth-6.8.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7d39d7f17e2c1ea7bdb3cfdd9c88f4c3b9fea3804c6ac2cc3674848e209d1898"},
{file = "habluetooth-6.8.3-cp313-cp313-win32.whl", hash = "sha256:339b8e712ed7e876817531b4f5041e5fcb43e8f5785c67171b5b7eaee08dbe0e"},
{file = "habluetooth-6.8.3-cp313-cp313-win_amd64.whl", hash = "sha256:9fb79bdf6edd3eaa45ff23c3595a4ecaa228b683b7639015ca1711ad7d123ad7"},
{file = "habluetooth-6.8.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:088098d414754e71e8c0cf39d719248284179a48abd1ce8cc0808759daabe41b"},
{file = "habluetooth-6.8.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6079ada3ba1010c125f71475280b98d6cc4d090dc751c22942ed5dd38efcab3b"},
{file = "habluetooth-6.8.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b6830be2cdbbb8e9f1a905f5c20623f735148a44ec27a353d45fca8f6e8c157e"},
{file = "habluetooth-6.8.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a375068d65445bc374b2f04ff35887a321fe23d0dacb675800fd11ac0ec9f4e"},
{file = "habluetooth-6.8.3-cp314-cp314-manylinux_2_41_x86_64.whl", hash = "sha256:cba7a43be63320020c24a549a2b4f0de574c9105c52237b82be5a5276ca763d5"},
{file = "habluetooth-6.8.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bc8f81a2669d9c008506f3783583dd749082f11031a83361f8dd6a4404583277"},
{file = "habluetooth-6.8.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:23d13c1de26d683123e8d1f58c683c28cb7833167b2efd5f5acb602d614bfeb3"},
{file = "habluetooth-6.8.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:09ac6205f294a9c6ffc9e58be93e5719f91852092f7eb76c159e17aece720815"},
{file = "habluetooth-6.8.3-cp314-cp314-win32.whl", hash = "sha256:cf9cc826ca1c3bb49349a52541648902e9c09af6407788b1c4d7f60fbc1c0661"},
{file = "habluetooth-6.8.3-cp314-cp314-win_amd64.whl", hash = "sha256:ae0bff9f7004c57834648e97621ba164a25a126080f66fb9625f91564f6cb0d2"},
{file = "habluetooth-6.8.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3eb387a7b6aad3906592251dd5fa9292342e4c8079f3305c9de8e6e735dc497b"},
{file = "habluetooth-6.8.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77322f033d9e0dcb873c8a6fb341bc8f38002160f5fa8ff066cade412a538266"},
{file = "habluetooth-6.8.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e13a75ab773d47f13a1ae977cff760802148cc01839d21c79628210f6e8a6784"},
{file = "habluetooth-6.8.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e899e6930d1d898bf64c7ae44580b86c58432c24850e5d09b2b72ba06a9ebc7"},
{file = "habluetooth-6.8.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e5f668cc71b5dc937045febc23f90fef16b39c367f42185b7903d8ac8c264806"},
{file = "habluetooth-6.8.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:776ac4ce53fb4652d8f671e303363ebbbf0544096941707d9d09fccb683328cb"},
{file = "habluetooth-6.8.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8ddcadf929276053e3cb2652ed18e032b029fcff88672cb5d1cdd045b9d6f700"},
{file = "habluetooth-6.8.3-cp314-cp314t-win32.whl", hash = "sha256:2cb0bfb61212bd738ef7300050770d9bed41e67bf029b7968e0498f3209cbad7"},
{file = "habluetooth-6.8.3-cp314-cp314t-win_amd64.whl", hash = "sha256:67bb922f6856cd983f4f388126d492badc4ece7f2940e19416e0c046afa73844"},
{file = "habluetooth-6.8.3.tar.gz", hash = "sha256:e2bc83250683fce51ab9c7e4f24e680d0e63ddcf946509218893f0c5e194ab52"},
]
[package.dependencies]
async-interrupt = ">=1.1.1"
bleak = ">=3.0.2"
bleak-retry-connector = ">=4.6.1"
bluetooth-adapters = ">=2.2.0"
bluetooth-auto-recovery = ">=1.6.4"
bluetooth-data-tools = ">=1.29.18"
btsocket = ">=0.3.0"
dbus-fast = {version = ">=5.0.16", markers = "platform_system == \"Linux\""}
[[package]]
name = "home-assistant-bluetooth"
version = "2.0.0"
description = "Home Assistant Bluetooth Models and Helpers"
optional = false
python-versions = ">=3.11"
groups = ["main"]
files = [
{file = "home_assistant_bluetooth-2.0.0-py3-none-any.whl", hash = "sha256:8d7c2ac48c303adacad73cb6cdfe7710f2fccd2a367d4c3106acf301b0d22b91"},
{file = "home_assistant_bluetooth-2.0.0.tar.gz", hash = "sha256:3febd16b194812b156024d9bc8ce8ec7622cdd8d159047cac504cc77ca034ea1"},
]
[package.dependencies]
habluetooth = ">=4.0"
[[package]]
name = "idna"
version = "3.18"
description = "Internationalized Domain Names in Applications (IDNA)"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2"},
{file = "idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848"},
]
[package.extras]
all = ["mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"]
[[package]]
name = "imagesize"
version = "1.5.0"
description = "Getting image size from png/jpeg/jpeg2000/gif file"
optional = true
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
groups = ["main"]
markers = "python_version >= \"3.15\" and extra == \"docs\""
files = [
{file = "imagesize-1.5.0-py2.py3-none-any.whl", hash = "sha256:32677681b3f434c2cb496f00e89c5a291247b35b1f527589909e008057da5899"},
{file = "imagesize-1.5.0.tar.gz", hash = "sha256:8bfc5363a7f2133a89f0098451e0bcb1cd71aba4dc02bbcecb39d99d40e1b94f"},
]
[[package]]
name = "imagesize"
version = "2.0.0"
description = "Get image size from headers (BMP/PNG/JPEG/JPEG2000/GIF/TIFF/SVG/Netpbm/WebP/AVIF/HEIC/HEIF)"
optional = true
python-versions = "<3.15,>=3.10"
groups = ["main"]
markers = "python_version < \"3.15\" and extra == \"docs\""
files = [
{file = "imagesize-2.0.0-py2.py3-none-any.whl", hash = "sha256:5667c5bbb57ab3f1fa4bc366f4fbc971db3d5ed011fd2715fd8001f782718d96"},
{file = "imagesize-2.0.0.tar.gz", hash = "sha256:8e8358c4a05c304f1fccf7ff96f036e7243a189e9e42e90851993c558cfe9ee3"},
]
[[package]]
name = "iniconfig"
version = "2.3.0"
description = "brain-dead simple config-ini parsing"
optional = false
python-versions = ">=3.10"
groups = ["dev"]
files = [
{file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"},
{file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"},
]
[[package]]
name = "isort"
version = "8.0.1"
description = "A Python utility / library to sort Python imports."
optional = false
python-versions = ">=3.10.0"
groups = ["dev"]
files = [
{file = "isort-8.0.1-py3-none-any.whl", hash = "sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75"},
{file = "isort-8.0.1.tar.gz", hash = "sha256:171ac4ff559cdc060bcfff550bc8404a486fee0caab245679c2abe7cb253c78d"},
]
[package.extras]
colors = ["colorama"]
[[package]]
name = "jinja2"
version = "3.1.6"
description = "A very fast and expressive template engine."
optional = true
python-versions = ">=3.7"
groups = ["main"]
markers = "extra == \"docs\""
files = [
{file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"},
{file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"},
]
[package.dependencies]
MarkupSafe = ">=2.0"
[package.extras]
i18n = ["Babel (>=2.7)"]
[[package]]
name = "librt"
version = "0.11.0"
description = "Mypyc runtime library"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
markers = "platform_python_implementation != \"PyPy\""
files = [
{file = "librt-0.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6e94ebfcfa2d5e9926d6c3b9aa4617ffc42a845b4321fb84021b872358c82a0f"},
{file = "librt-0.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ae627397a2f351560440d872d6f7c8dbb4072e57868e7b2fc5b8b430fe489d45"},
{file = "librt-0.11.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc329359321b67d24efdf4bc69012b0597001649544db662c001db5a0184794c"},
{file = "librt-0.11.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:7e82e642ab0f7608ce2fe53d76ca2280a9ee33a1b06556142c7c6fe80a86fc33"},
{file = "librt-0.11.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:88145c15c67731d54283d135b03244028c750cc9edc334a96a4f5950ebdb2884"},
{file = "librt-0.11.0-cp310-cp310-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9d36a51b3d93320b686588e27123f4995804dbf1bce81df78c02fc3c6eea9280"},
{file = "librt-0.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d00f3ac06a2a8b246327f11e186a53a100a4d5c7ed52346367e5ec751d51586c"},
{file = "librt-0.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:461bbceede621f1ffb8839755f8663e886087ee7af16294cab7fb4d782c62eeb"},
{file = "librt-0.11.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0cad8a4d6a8ff03c9b76f9414caccd78e7cfbc8a2e12fa334d8e1d9932753783"},
{file = "librt-0.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f37aa505b3cf60701562eddb32df74b12a9e380c207fd8b06dd157a943ac7ea0"},
{file = "librt-0.11.0-cp310-cp310-win32.whl", hash = "sha256:94663a21534637f0e787ec2a2a756022df6e5b7b2335a5cdd7d8e33d68a2af89"},
{file = "librt-0.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:dec7db73758c2b54953fd8b7fe348c45188fe26b39ee18446196edd08453a5d4"},
{file = "librt-0.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:93d95bd45b7d58343d8b90d904450a545144eec19a002511163426f8ab1fae29"},
{file = "librt-0.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ee278c769a713638cdacd4c0436d72156e75df3ebc0166ab2b9dc43acc386c9"},
{file = "librt-0.11.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f230cb1cbc9faaa616f9a678f530ebcf186e414b6bcbd88b960e4ba1b92428d5"},
{file = "librt-0.11.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5d63c855d86938d9de93e265c9bd8c705b51ec494de5738340ee93767a686e4b"},
{file = "librt-0.11.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:993f028be9e96a08d31df3479ac80d99be374d17f3b78e4796b3fd3c913d4e89"},
{file = "librt-0.11.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:258d73a0aa66a055e65b2e4d1b8cdb23b9d132c5bb915d9547d804fcaed116cc"},
{file = "librt-0.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0827efe7854718f04aaddf6496e96960a956e676fe1d0f04eb41511fd8ad06d5"},
{file = "librt-0.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7753e57d6e12d019c0d8786f1c09c709f4c3fcc57c3887b24e36e6c06ec938b7"},
{file = "librt-0.11.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:11bd19822431cc21af9f27374e7ae2e58103c7d98bda823536a6c47f6bb2bb3d"},
{file = "librt-0.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:22bdf239b219d3993761a148ffa134b19e52e9989c84f845d5d7b71d70a17412"},
{file = "librt-0.11.0-cp311-cp311-win32.whl", hash = "sha256:46c60b61e308eb535fbd6fa622b1ee1bb2815691c1ad9c98bf7b84952ec3bc8d"},
{file = "librt-0.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:902e546ff044f579ff1c953ff5fce97b636fe9e3943996b2177710c6ef076f73"},
{file = "librt-0.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:65ac3bc20f78aa0ee5ae84baa68917f89fef4af63e941084dd019a0d0e749f0c"},
{file = "librt-0.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b87504f1690a23b9a2cca841191a04f83895d4fc2dd04df91d82b1a04ca2ad46"},
{file = "librt-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40071fc5fe0ce8daa6de616702314a01e1250711682b0523d6ab8d4525910cb3"},
{file = "librt-0.11.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:137e79445c896a0ea7b265f52d23954e05b64222ee1af69e2cb34219067cbb67"},
{file = "librt-0.11.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:cca6644054e78746d8d4ef238681f9c34ff8b584fe6b988ecebb8db3b15e622a"},
{file = "librt-0.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5b0eea49f5562861ee8d757a32ef7d559c1d35be2aaaa1ec28941d74c9ffc8a"},
{file = "librt-0.11.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0d1029d7e1ae1a7e647ed6fb5df8c4ce2dffefb7a9f5fd1376a4554d96dac09f"},
{file = "librt-0.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bc3ce6b33c5828d9e80592011a5c584cb2ce86edbc4088405f70da47dc1d1b3b"},
{file = "librt-0.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:936c5995f3514a42111f20099397d8177c79b4d7e70961e396c6f5a0a3566766"},
{file = "librt-0.11.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9bc0ca6ad9381cbe8e4aa6e5726e4c80c78115a6e9723c599ed1d73e092bc49d"},
{file = "librt-0.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:070aa8c26c0a74774317a72df8851facc7f0f012a5b406557ac56992d92e1ec8"},
{file = "librt-0.11.0-cp312-cp312-win32.whl", hash = "sha256:6bf14feb84b05ae945277395451998c89c54d0def4070eb5c08de544930b245a"},
{file = "librt-0.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:75672f0bc524ede266287d532d7923dbce94c7514ad07627bac3d0c6d92cc4d9"},
{file = "librt-0.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:2f10cf143e4a9bb0f4f5af568a00df94a2d69ef41c2579584454bb0fe5cc642c"},
{file = "librt-0.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:78dc31f7fdfe9c9d0eb0e8f42d139db230e826415bbcabd9f0e9faaaee909894"},
{file = "librt-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fa475675db22290c3158e1d42326d0f5a65f04f44a0e68c3630a25b53560fb9c"},
{file = "librt-0.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:621db29691044bdeda22e789e482e1b0f3a985d90e3426c9c6d17606416205ea"},
{file = "librt-0.11.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:a9010e2ed5b3a9e158c5fd966b3ab7e834bb3d3aacc8f66c91dd4b57a3799230"},
{file = "librt-0.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c39513d8b7477a2e1ed8c43fc21c524e8d5a0f8d4e8b7b074dbdbe7820a08e2"},
{file = "librt-0.11.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7aef3cf1d5af86e770ab04bfd993dfc4ae8b8c17f66fb77dd4a7d50de7bbb1a3"},
{file = "librt-0.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:557183ddc36babe46b27dd60facbd5adb4492181a5be887587d57cda6e092f21"},
{file = "librt-0.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:83d3e1f72bd42f6c5c0b7daec530c3f829bd02db42c70b8ddf0c2d90a2459930"},
{file = "librt-0.11.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:4ce1f21fbe589bc1afd7872dece84fb0e1144f794a288e58a10d2c54a55c43be"},
{file = "librt-0.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b09f7044ea2b64c9da42fd3d335666518cfd1c6e8a182c95da73d0214b41e"},
{file = "librt-0.11.0-cp313-cp313-win32.whl", hash = "sha256:78fddc31cd4d3caa897ad5d31f856b1faadc9474021ad6cb182b9018793e254e"},
{file = "librt-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ca8aa88751a775870b764e93bad5135385f563cb8dcee399abf034ea4d3cb47"},
{file = "librt-0.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:96f044bb325fd9cf1a723015638c219e9143f0dfbc0ca54c565df2b7fc748b44"},
{file = "librt-0.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4a017a95e5837dc15a8c5661d60e05daa96b90908b1aa6b7acdf443cd25c8ebd"},
{file = "librt-0.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b1ecbd9819deccc39b7542bf4d2a740d8a620694d39989e58661d3763458f8d4"},
{file = "librt-0.11.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7da327dacd7be8f8ec36547373550744a3cc0e536d54665cd83f8bcd961200e8"},
{file = "librt-0.11.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:0dc56b1f8d06e60db362cc3fdae206681817f86ce4725d34511473487f12a34b"},
{file = "librt-0.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05fb8fb2ab90e21c8d12ea240d744ad514da9baf381ebfa70d91d20d21713175"},
{file = "librt-0.11.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cae74872be221df4374d10fec61f93ed1513b9546ea84f2c0bf73ab3e9bd0b03"},
{file = "librt-0.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:32bcc918c0148eb7e3d57385125bac7e5f9e4359d05f07448b09f6f778c2f31c"},
{file = "librt-0.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f9743fc99135d5f78d2454435615f6dec0473ca507c26ce9d92b10b562a280d3"},
{file = "librt-0.11.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5ba067f4aadae8fda802d91d2124c90c42195ff32d9161d3549e6d05cfe26f96"},
{file = "librt-0.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:de3bf945454d032f9e390b85c4072e0a0570bf825421c8be0e71209fa65e1abe"},
{file = "librt-0.11.0-cp314-cp314-win32.whl", hash = "sha256:d2277a05f6dcb9fd13db9566aac4fabd68c3ea1ea46ee5567d4eef8efa495a2f"},
{file = "librt-0.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:ab73e8db5e3f564d812c1f5c3a175930a5f9bc96ccb5e3b22a34d7858b401cf7"},
{file = "librt-0.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:aea3caa317752e3a466fa8af45d91ee0ea8c7fdd96e42b0a8dd9b76a7931eba1"},
{file = "librt-0.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d1b36540d7aaf9b9101b3a6f376c8d8e9f7a9aec93ed05918f2c69d493ffef72"},
{file = "librt-0.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:efbb343ab2ce3540f4ecbe6315d677ed70f37cd9a72b1e58066c918ca83acbaa"},
{file = "librt-0.11.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0dd688aab3f7914d3e6e5e3554978e0383312fb8e771d84be008a35b9ee548"},
{file = "librt-0.11.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:f5fb36b8c6c63fdcbb1d526d94c0d1331610d43f4118cc1beb4efef4f3faacb2"},
{file = "librt-0.11.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4a9a237d13addb93715b6fee74023d5ee3469b53fce527626c0e088aa585805f"},
{file = "librt-0.11.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5ddd17bd87b2c56ddd60e546a7984a2e64c4e8eab92fb4cf3830a48ad5469d51"},
{file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd43992b4473d42f12ff9e68326079f0696d9d4e6000e8f39a0238d482ba6ee2"},
{file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:f8e3e8056dd674e279741485e2e512d6e9a751c7455809d0114e6ebf8d781085"},
{file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c1f708d8ae9c56cf38a903c44297243d2ec83fd82b396b977e0144a3e76217e3"},
{file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0add982e0e7b9fc14cf4b33789d5f13f66581889b88c2f58099f6ce8f92617bd"},
{file = "librt-0.11.0-cp314-cp314t-win32.whl", hash = "sha256:2b481d846ac894c4e8403c5fd0e87c5d11d6499e404b474602508a224ff531c8"},
{file = "librt-0.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:28edb433edde181112a908c78907af28f964eabc15f4dd16c9d66c834302677c"},
{file = "librt-0.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dee008f20b542e3cd162ba338a7f9ec0f6d23d395f66fe8aeeec3c9d067ea253"},
{file = "librt-0.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6bd72d903911d995ab666dbd1871f8b1e80925a699af8063fbf50053329fb05f"},
{file = "librt-0.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ef69ac715f3cd8e5cd252cb2aebfa72c015492aacc339d5d7bf8fef3c62c677"},
{file = "librt-0.11.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:624a40c4a4ad7773315c287276cd024509b2c66ff5904f504bfc08d2c70293ab"},
{file = "librt-0.11.0-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:41dc19fe150b69716c8ece4f76773a9e8813fe3e35e032a58b4d46423fb8d7c0"},
{file = "librt-0.11.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4e8bd98ea9c47ae90b319a087ab28dac493f1ffbc1ecd1f28fcdbf3b7e1108d1"},
{file = "librt-0.11.0-cp39-cp39-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:84308fc49423ce6475d1c5d1985cd69a8ca9f0325fc7d5f81bb690a3f3625d4e"},
{file = "librt-0.11.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ff0fbaf5f44a21beeb0110f2ab64f45135a9536a834b79c0d1ef018f2786bbfa"},
{file = "librt-0.11.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9c028a9442a18e266955d364ce42259136e79a7ba14d773e0d778d5f70cd56f1"},
{file = "librt-0.11.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:9f1692105a02bcf853f355032a5fdc5494358ef83d8fd22d16de375c85cec3f5"},
{file = "librt-0.11.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7a80a71e1fda83cc752a9141e87aae7fef279538597564d670e9ce513f286192"},
{file = "librt-0.11.0-cp39-cp39-win32.whl", hash = "sha256:140695816ddf3c86eb972981a26f35efd871c44b0c3aed44c8cd01749386617f"},
{file = "librt-0.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:92f7ff819c197fc30473190a12c2856f325ac90aabfccbeb2072d28cc2e234e3"},
{file = "librt-0.11.0.tar.gz", hash = "sha256:075dc3ef4458a278e0195cbf6ac9d38808d9b906c5a6c7f7f79c3888276a3fb1"},
]
[[package]]
name = "markdown-it-py"
version = "4.2.0"
description = "Python port of markdown-it. Markdown parsing, done right!"
optional = true
python-versions = ">=3.10"
groups = ["main"]
markers = "extra == \"docs\""
files = [
{file = "markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a"},
{file = "markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49"},
]
[package.dependencies]
mdurl = ">=0.1,<1.0"
[package.extras]
benchmarking = ["psutil", "pytest", "pytest-benchmark"]
compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "markdown-it-pyrs", "mistletoe (>=1.0,<2.0)", "mistune (>=3.0,<4.0)", "panflute (>=2.3,<3.0)"]
linkify = ["linkify-it-py (>=1,<3)"]
plugins = ["mdit-py-plugins (>=0.5.0)"]
profiling = ["gprof2dot"]
rtd = ["ipykernel", "jupyter_sphinx", "mdit-py-plugins (>=0.5.0)", "myst-parser", "pyyaml", "sphinx", "sphinx-book-theme (>=1.0,<2.0)", "sphinx-copybutton", "sphinx-design"]
testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions", "pytest-timeout", "requests"]
[[package]]
name = "markupsafe"
version = "3.0.3"
description = "Safely add untrusted strings to HTML/XML markup."
optional = true
python-versions = ">=3.9"
groups = ["main"]
markers = "extra == \"docs\""
files = [
{file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"},
{file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"},
{file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695"},
{file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591"},
{file = "markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c"},
{file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f"},
{file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6"},
{file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1"},
{file = "markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa"},
{file = "markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8"},
{file = "markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1"},
{file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"},
{file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"},
{file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"},
{file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"},
{file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"},
{file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"},
{file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"},
{file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"},
{file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"},
{file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"},
{file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"},
{file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"},
{file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"},
{file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"},
{file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"},
{file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"},
{file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"},
{file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"},
{file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"},
{file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"},
{file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"},
{file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"},
{file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"},
{file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"},
{file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"},
{file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"},
{file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"},
{file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"},
{file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"},
{file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"},
{file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"},
{file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"},
{file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"},
{file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"},
{file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"},
{file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"},
{file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"},
{file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"},
{file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"},
{file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"},
{file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"},
{file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"},
{file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"},
{file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"},
{file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"},
{file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"},
{file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"},
{file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"},
{file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"},
{file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"},
{file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"},
{file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"},
{file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"},
{file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"},
{file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"},
{file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"},
{file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"},
{file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"},
{file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"},
{file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"},
{file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"},
{file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"},
{file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"},
{file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"},
{file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"},
{file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"},
{file = "markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26"},
{file = "markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc"},
{file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c"},
{file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42"},
{file = "markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b"},
{file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758"},
{file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2"},
{file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d"},
{file = "markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7"},
{file = "markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e"},
{file = "markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8"},
{file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"},
]
[[package]]
name = "mccabe"
version = "0.7.0"
description = "McCabe checker, plugin for flake8"
optional = false
python-versions = ">=3.6"
groups = ["dev"]
files = [
{file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
]
[[package]]
name = "mdit-py-plugins"
version = "0.6.1"
description = "Collection of plugins for markdown-it-py"
optional = true
python-versions = ">=3.10"
groups = ["main"]
markers = "extra == \"docs\""
files = [
{file = "mdit_py_plugins-0.6.1-py3-none-any.whl", hash = "sha256:214c82fb2ac524472ab6a5bcab1de80f73b50443e187f401bfd77efbc7c6481d"},
{file = "mdit_py_plugins-0.6.1.tar.gz", hash = "sha256:a2bca0f039f39dbd35fb74ae1b5f998608c437463371f0ff7f49a19a17a114d0"},
]
[package.dependencies]
markdown-it-py = ">=2.0.0,<5.0.0"
[package.extras]
code-style = ["pre-commit"]
rtd = ["myst-parser", "sphinx-book-theme"]
testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions", "pytest-timeout"]
[[package]]
name = "mdurl"
version = "0.1.2"
description = "Markdown URL utilities"
optional = true
python-versions = ">=3.7"
groups = ["main"]
markers = "extra == \"docs\""
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 = "multidict"
version = "6.7.1"
description = "multidict implementation"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "multidict-6.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c93c3db7ea657dd4637d57e74ab73de31bccefe144d3d4ce370052035bc85fb5"},
{file = "multidict-6.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:974e72a2474600827abaeda71af0c53d9ebbc3c2eb7da37b37d7829ae31232d8"},
{file = "multidict-6.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdea2e7b2456cfb6694fb113066fd0ec7ea4d67e3a35e1f4cbeea0b448bf5872"},
{file = "multidict-6.7.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17207077e29342fdc2c9a82e4b306f1127bf1ea91f8b71e02d4798a70bb99991"},
{file = "multidict-6.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4f49cb5661344764e4c7c7973e92a47a59b8fc19b6523649ec9dc4960e58a03"},
{file = "multidict-6.7.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a9fc4caa29e2e6ae408d1c450ac8bf19892c5fca83ee634ecd88a53332c59981"},
{file = "multidict-6.7.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c5f0c21549ab432b57dcc82130f388d84ad8179824cc3f223d5e7cfbfd4143f6"},
{file = "multidict-6.7.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7dfb78d966b2c906ae1d28ccf6e6712a3cd04407ee5088cd276fe8cb42186190"},
{file = "multidict-6.7.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b0d9b91d1aa44db9c1f1ecd0d9d2ae610b2f4f856448664e01a3b35899f3f92"},
{file = "multidict-6.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dd96c01a9dcd4889dcfcf9eb5544ca0c77603f239e3ffab0524ec17aea9a93ee"},
{file = "multidict-6.7.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:067343c68cd6612d375710f895337b3a98a033c94f14b9a99eff902f205424e2"},
{file = "multidict-6.7.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5884a04f4ff56c6120f6ccf703bdeb8b5079d808ba604d4d53aec0d55dc33568"},
{file = "multidict-6.7.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8affcf1c98b82bc901702eb73b6947a1bfa170823c153fe8a47b5f5f02e48e40"},
{file = "multidict-6.7.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0d17522c37d03e85c8098ec8431636309b2682cf12e58f4dbc76121fb50e4962"},
{file = "multidict-6.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24c0cf81544ca5e17cfcb6e482e7a82cd475925242b308b890c9452a074d4505"},
{file = "multidict-6.7.1-cp310-cp310-win32.whl", hash = "sha256:d82dd730a95e6643802f4454b8fdecdf08667881a9c5670db85bc5a56693f122"},
{file = "multidict-6.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf37cbe5ced48d417ba045aca1b21bafca67489452debcde94778a576666a1df"},
{file = "multidict-6.7.1-cp310-cp310-win_arm64.whl", hash = "sha256:59bc83d3f66b41dac1e7460aac1d196edc70c9ba3094965c467715a70ecb46db"},
{file = "multidict-6.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ff981b266af91d7b4b3793ca3382e53229088d193a85dfad6f5f4c27fc73e5d"},
{file = "multidict-6.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:844c5bca0b5444adb44a623fb0a1310c2f4cd41f402126bb269cd44c9b3f3e1e"},
{file = "multidict-6.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f2a0a924d4c2e9afcd7ec64f9de35fcd96915149b2216e1cb2c10a56df483855"},
{file = "multidict-6.7.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8be1802715a8e892c784c0197c2ace276ea52702a0ede98b6310c8f255a5afb3"},
{file = "multidict-6.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e2d2ed645ea29f31c4c7ea1552fcfd7cb7ba656e1eafd4134a6620c9f5fdd9e"},
{file = "multidict-6.7.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:95922cee9a778659e91db6497596435777bd25ed116701a4c034f8e46544955a"},
{file = "multidict-6.7.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6b83cabdc375ffaaa15edd97eb7c0c672ad788e2687004990074d7d6c9b140c8"},
{file = "multidict-6.7.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:38fb49540705369bab8484db0689d86c0a33a0a9f2c1b197f506b71b4b6c19b0"},
{file = "multidict-6.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:439cbebd499f92e9aa6793016a8acaa161dfa749ae86d20960189f5398a19144"},
{file = "multidict-6.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6d3bc717b6fe763b8be3f2bee2701d3c8eb1b2a8ae9f60910f1b2860c82b6c49"},
{file = "multidict-6.7.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:619e5a1ac57986dbfec9f0b301d865dddf763696435e2962f6d9cf2fdff2bb71"},
{file = "multidict-6.7.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0b38ebffd9be37c1170d33bc0f36f4f262e0a09bc1aac1c34c7aa51a7293f0b3"},
{file = "multidict-6.7.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:10ae39c9cfe6adedcdb764f5e8411d4a92b055e35573a2eaa88d3323289ef93c"},
{file = "multidict-6.7.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:25167cc263257660290fba06b9318d2026e3c910be240a146e1f66dd114af2b0"},
{file = "multidict-6.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:128441d052254f42989ef98b7b6a6ecb1e6f708aa962c7984235316db59f50fa"},
{file = "multidict-6.7.1-cp311-cp311-win32.whl", hash = "sha256:d62b7f64ffde3b99d06b707a280db04fb3855b55f5a06df387236051d0668f4a"},
{file = "multidict-6.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:bdbf9f3b332abd0cdb306e7c2113818ab1e922dc84b8f8fd06ec89ed2a19ab8b"},
{file = "multidict-6.7.1-cp311-cp311-win_arm64.whl", hash = "sha256:b8c990b037d2fff2f4e33d3f21b9b531c5745b33a49a7d6dbe7a177266af44f6"},
{file = "multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172"},
{file = "multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd"},
{file = "multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7"},
{file = "multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53"},
{file = "multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75"},
{file = "multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b"},
{file = "multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733"},
{file = "multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a"},
{file = "multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961"},
{file = "multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582"},
{file = "multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e"},
{file = "multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3"},
{file = "multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6"},
{file = "multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a"},
{file = "multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba"},
{file = "multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511"},
{file = "multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19"},
{file = "multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf"},
{file = "multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23"},
{file = "multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2"},
{file = "multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445"},
{file = "multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177"},
{file = "multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23"},
{file = "multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060"},
{file = "multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d"},
{file = "multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed"},
{file = "multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429"},
{file = "multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6"},
{file = "multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9"},
{file = "multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c"},
{file = "multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84"},
{file = "multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d"},
{file = "multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33"},
{file = "multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3"},
{file = "multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5"},
{file = "multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df"},
{file = "multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1"},
{file = "multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963"},
{file = "multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34"},
{file = "multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65"},
{file = "multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292"},
{file = "multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43"},
{file = "multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca"},
{file = "multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd"},
{file = "multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7"},
{file = "multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3"},
{file = "multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4"},
{file = "multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8"},
{file = "multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c"},
{file = "multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52"},
{file = "multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108"},
{file = "multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32"},
{file = "multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8"},
{file = "multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118"},
{file = "multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee"},
{file = "multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2"},
{file = "multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1"},
{file = "multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d"},
{file = "multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31"},
{file = "multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048"},
{file = "multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362"},
{file = "multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37"},
{file = "multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709"},
{file = "multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0"},
{file = "multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb"},
{file = "multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd"},
{file = "multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601"},
{file = "multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1"},
{file = "multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b"},
{file = "multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d"},
{file = "multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f"},
{file = "multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5"},
{file = "multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581"},
{file = "multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a"},
{file = "multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c"},
{file = "multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262"},
{file = "multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59"},
{file = "multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889"},
{file = "multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4"},
{file = "multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d"},
{file = "multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609"},
{file = "multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489"},
{file = "multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c"},
{file = "multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e"},
{file = "multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c"},
{file = "multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9"},
{file = "multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2"},
{file = "multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7"},
{file = "multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5"},
{file = "multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2"},
{file = "multidict-6.7.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:65573858d27cdeaca41893185677dc82395159aa28875a8867af66532d413a8f"},
{file = "multidict-6.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c524c6fb8fc342793708ab111c4dbc90ff9abd568de220432500e47e990c0358"},
{file = "multidict-6.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:aa23b001d968faef416ff70dc0f1ab045517b9b42a90edd3e9bcdb06479e31d5"},
{file = "multidict-6.7.1-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6704fa2b7453b2fb121740555fa1ee20cd98c4d011120caf4d2b8d4e7c76eec0"},
{file = "multidict-6.7.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:121a34e5bfa410cdf2c8c49716de160de3b1dbcd86b49656f5681e4543bcd1a8"},
{file = "multidict-6.7.1-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:026d264228bcd637d4e060844e39cdc60f86c479e463d49075dedc21b18fbbe0"},
{file = "multidict-6.7.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0e697826df7eb63418ee190fd06ce9f1803593bb4b9517d08c60d9b9a7f69d8f"},
{file = "multidict-6.7.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb08271280173720e9fea9ede98e5231defcbad90f1624bea26f32ec8a956e2f"},
{file = "multidict-6.7.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6b3228e1d80af737b72925ce5fb4daf5a335e49cd7ab77ed7b9fdfbf58c526e"},
{file = "multidict-6.7.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3943debf0fbb57bdde5901695c11094a9a36723e5c03875f87718ee15ca2f4d2"},
{file = "multidict-6.7.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:98c5787b0a0d9a41d9311eae44c3b76e6753def8d8870ab501320efe75a6a5f8"},
{file = "multidict-6.7.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:08ccb2a6dc72009093ebe7f3f073e5ec5964cba9a706fa94b1a1484039b87941"},
{file = "multidict-6.7.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb351f72c26dc9abe338ca7294661aa22969ad8ffe7ef7d5541d19f368dc854a"},
{file = "multidict-6.7.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ac1c665bad8b5d762f5f85ebe4d94130c26965f11de70c708c75671297c776de"},
{file = "multidict-6.7.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fa6609d0364f4f6f58351b4659a1f3e0e898ba2a8c5cac04cb2c7bc556b0bc5"},
{file = "multidict-6.7.1-cp39-cp39-win32.whl", hash = "sha256:6f77ce314a29263e67adadc7e7c1bc699fcb3a305059ab973d038f87caa42ed0"},
{file = "multidict-6.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:f537b55778cd3cbee430abe3131255d3a78202e0f9ea7ffc6ada893a4bcaeea4"},
{file = "multidict-6.7.1-cp39-cp39-win_arm64.whl", hash = "sha256:749aa54f578f2e5f439538706a475aa844bfa8ef75854b1401e6e528e4937cf9"},
{file = "multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56"},
{file = "multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d"},
]
[[package]]
name = "mypy"
version = "2.1.0"
description = "Optional static typing for Python"
optional = false
python-versions = ">=3.10"
groups = ["dev"]
files = [
{file = "mypy-2.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:11a6beb180257a805961aea9ec591bbd0bd17f1e18d35b8456d57aee5bedfedc"},
{file = "mypy-2.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8ef78c1d306bbf9a8a12f526c44902c9c28dffd6c52c52bf6a72641ce18d3849"},
{file = "mypy-2.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c209a90853081ff01d01ee895cafe10f7db1474e0d95beaeef0f6c1db9119bbd"},
{file = "mypy-2.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47cebf61abde7c088a4e27718a8b13a81655686b2e9c251f5c0915a802248166"},
{file = "mypy-2.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d57a90ae5e872138a425ec328edbc9b235d1934c4377881a33ec05b341acc9a8"},
{file = "mypy-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:aea7f7a8a55b459c34275fc468ada6ca7c173a5e43a68f5dbe588a563d8a06b8"},
{file = "mypy-2.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:c989640253f0d76843e9c6c1bbf4bd48c5e85ada61bde4beb37cb3eca035685e"},
{file = "mypy-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a683016b16fe2f572dc04c72be7ee0504ac1605a265d0200f5cea695fb788f41"},
{file = "mypy-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a293c534adb55271fef24a26da04b855540a8c13cc07bc5917b9fd2c394f2ca"},
{file = "mypy-2.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7406f4d048e71e576f5356d317e5b0a9e666dfd966bd99f9d14ca06e1a341538"},
{file = "mypy-2.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e0210d626fc8b31ccc90233754c7bc90e1f43205e85d96387f7db1285b55c398"},
{file = "mypy-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3712c20deed54e814eaaa825603bada8ea1c390670a397c95b98405347acc563"},
{file = "mypy-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fcaa0e479066e31f7cceb6a3bea39cb22b2ff51a6b2f24f193d19179ba17c389"},
{file = "mypy-2.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:0b1a5260c95aa443083f9ed3592662941951bca3d4ca224a5dc517c38b7cf666"},
{file = "mypy-2.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:244358bf1c0da7722230bce60683d52e8e9fd030554926f15b747a84efb5b3af"},
{file = "mypy-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4ec7c57657493c7a75534df2751c8ae2cda383c16ecc55d2106c54476b1b16f6"},
{file = "mypy-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8161b6ff4392410023224f0969d17db93e1e154bc3e4ba62598e720723ae211"},
{file = "mypy-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf03e12003084a67395184d3eb8cbd6a489dc3655b5664b28c210a9e2403ab0b"},
{file = "mypy-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:20509760fd791c51579d573153407d226385ec1f8bcce55d730b354f3336bc22"},
{file = "mypy-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:6753d0c1fdd6b1a23b9e4f283ce80b2153b724adcb2653b20b85a8a28ac6436b"},
{file = "mypy-2.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:98ebb6589bb3b6d0c6f0c459d53ca55b8091fbc13d277c4041c885392e8195e8"},
{file = "mypy-2.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35aac3bb114e03888f535d5eb51b8bafbb3266586b599da1940f9b1be3ec5bd5"},
{file = "mypy-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8de55a8c861f2a49331f807be98d90caeceeef520bde13d43a160207f8af613e"},
{file = "mypy-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5fdf2941a07434af755837d9880f7d7d25f1dacb1af9dcd4b9b66f2220a3024e"},
{file = "mypy-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e195b817c13f02352a9c124301f9f30f078405444679b6753c1b96b6eed37285"},
{file = "mypy-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5431d42af987ebd92ba2f71d45c85ed41d8e6ca9f5fd209a69f68f707d2469e5"},
{file = "mypy-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:767fe8c66dc3e01e19e1737d4c38ebefead16125e1b8e58ad421903b376f5c65"},
{file = "mypy-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:ecfe70d43775ab99562ab128ce49854a362044c9f894961f68f898c23cb7429d"},
{file = "mypy-2.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:7354c5a7f69d9345c3d6e69921d57088eea3ddeeb6b20d34c1b3855b02c36ec2"},
{file = "mypy-2.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:49890d4f76ac9e06ec117f9e09f3174da70a620a0c300953d8595c926e80947f"},
{file = "mypy-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:761be68e023ef5d94678772396a8af1220030f80837a3afd8d0aef3b419666f4"},
{file = "mypy-2.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c90345fc182dc363b891350457ec69c35140858538f38b4540845afcc32b1aef"},
{file = "mypy-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b84802e7b5a6daf1f5e15bc9fcd7ddae77be13981ffab037f1c67bb84d67d135"},
{file = "mypy-2.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:022c771234936ceac541ebaf836fe9e2abeb3f5e09aff21588fe543ff006fe21"},
{file = "mypy-2.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:498207db725cec88829a6a5c2fc771205fd043719ef98bc49aba8fb9fc4e6d57"},
{file = "mypy-2.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7d5e5cad0efeba72b93cd17490cc0d69c5ac9ca132994fe3fb0314808aeeb83e"},
{file = "mypy-2.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ff715050c127d724fd260a2e666e7747fdd83511c0c47d449d98238970aef780"},
{file = "mypy-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:82208da9e09414d520e912d3e462d454854bed0810b71540bb016dcbca7308fd"},
{file = "mypy-2.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e79ebc1b904b84f0310dff7469655a9c36c7a68bddb37bdd42b67a332df61d08"},
{file = "mypy-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e583edc957cfb0deb142079162ae826f58449b116c1d442f2d91c69d9fced081"},
{file = "mypy-2.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b33b6cd332695bba180d55e717a79d3038e479a2c49cc5eb3d53603409b9a5d7"},
{file = "mypy-2.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:4f910fe825376a7b66ef7ca8c98e5a149e8cd64c19ae71d84047a74ee060d4e6"},
{file = "mypy-2.1.0-py3-none-any.whl", hash = "sha256:a663814603a5c563fb87a4f96fb473eeb30d1f5a4885afcf44f9db000a366289"},
{file = "mypy-2.1.0.tar.gz", hash = "sha256:81e76ad12c2d804512e9b13240d1588316531bfba07558286078bfbce9613633"},
]
[package.dependencies]
ast-serialize = ">=0.3.0,<1.0.0"
librt = {version = ">=0.11.0", markers = "platform_python_implementation != \"PyPy\""}
mypy_extensions = ">=1.0.0"
pathspec = ">=1.0.0"
typing_extensions = [
{version = ">=4.6.0", markers = "python_version < \"3.15\""},
{version = ">=4.14.0", markers = "python_version >= \"3.15\""},
]
[package.extras]
dmypy = ["psutil (>=4.0)"]
faster-cache = ["orjson"]
install-types = ["pip"]
mypyc = ["setuptools (>=50)"]
reports = ["lxml"]
[[package]]
name = "mypy-extensions"
version = "1.1.0"
description = "Type system extensions for programs checked with the mypy type checker."
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"},
{file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"},
]
[[package]]
name = "myst-parser"
version = "5.1.0"
description = "An extended [CommonMark](https://spec.commonmark.org/) compliant parser,"
optional = true
python-versions = ">=3.11"
groups = ["main"]
markers = "extra == \"docs\""
files = [
{file = "myst_parser-5.1.0-py3-none-any.whl", hash = "sha256:9c91c52b3cdb4d94a6506e4fab4e2f296c7623a0da0dcbe6de1565c3dad67a8a"},
{file = "myst_parser-5.1.0.tar.gz", hash = "sha256:ab69322dc6719dcc7f296479dbb70181b66df6ed315064f92dbc85c0e1bf2f02"},
]
[package.dependencies]
docutils = ">=0.20,<0.23"
jinja2 = "*"
markdown-it-py = ">=4.2,<5.0"
mdit-py-plugins = ">=0.6.1,<1.0"
pyyaml = "*"
sphinx = ">=8,<10"
[package.extras]
code-style = ["pre-commit (>=4.0,<5.0)"]
linkify = ["linkify-it-py (>=2.0,<3.0)"]
rtd = ["ipython", "sphinx (>=8)", "sphinx-autodoc2 (>=0.5.0,<0.6.0)", "sphinx-book-theme (>=1.1,<2.0)", "sphinx-copybutton", "sphinx-design", "sphinx-pyscript", "sphinx-tippy (>=0.4.3)", "sphinx-togglebutton", "sphinxext-opengraph (>=0.13.0,<0.14.0)", "sphinxext-rediraffe (>=0.3.0,<0.4.0)"]
testing = ["beautifulsoup4", "coverage[toml]", "defusedxml", "pygments (<2.21)", "pytest (>=9,<10)", "pytest-cov", "pytest-param-files (>=0.6.0,<0.7.0)", "pytest-regressions", "sphinx-pytest (>=0.3.0,<0.4.0)"]
testing-docutils = ["pygments", "pytest (>=9,<10)", "pytest-param-files (>=0.6.0,<0.7.0)"]
[[package]]
name = "orjson"
version = "3.11.9"
description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "orjson-3.11.9-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:135869ef917b8704ea0a94e01620e0c05021c15c52036e4663baffe75e72f8ce"},
{file = "orjson-3.11.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:115ab5f5f4a0f203cc2a5f0fb09aee503a3f771aa08392949ab5ca230c4fbdbd"},
{file = "orjson-3.11.9-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4da3c38a2083ca4aaf9c2a36776cce3e9328e6647b10d118948f3cfb4913ffe4"},
{file = "orjson-3.11.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53b50b0e14084b8f7e29c5ce84c5af0f1160169b30d8a6914231d97d2fe297d4"},
{file = "orjson-3.11.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:231742b4a11dad8d5380a435962c57e91b7c37b79be858f4ef1c0df1a259897e"},
{file = "orjson-3.11.9-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:34fd2317602587321faab75ab76c623a0117e80841a6413654f04e47f339a8fb"},
{file = "orjson-3.11.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f3db16e69b667b132e0f305a833d5497da302d801508cbb051ed9a9819da47"},
{file = "orjson-3.11.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0b34789fa0da61cf7bef0546b09c738fb195331e017e477096d129e9105ab03d"},
{file = "orjson-3.11.9-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:87e4d4ab280b0c87424d47695bec2182caf8cfc17879ea78dab76680194abc13"},
{file = "orjson-3.11.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ace6c58523302d3b97b6ac5c38a5298a54b473762b6be82726b4265c41029f92"},
{file = "orjson-3.11.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:97d0d932803c1b164fde11cb542a9efcb1e0f63b184537cca65887147906ff48"},
{file = "orjson-3.11.9-cp310-cp310-win32.whl", hash = "sha256:b3afcf569c15577a9fe64627292daa3e6b3a70f4fb77a5df246a87ec21681b94"},
{file = "orjson-3.11.9-cp310-cp310-win_amd64.whl", hash = "sha256:8697ab6a080a5c46edaad50e2bc5bd8c7ca5c66442d24104fa44ec74910a8244"},
{file = "orjson-3.11.9-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f01c4818b3fc9b0da8e096722a84318071eaa118df35f6ed2344da0e73a5444f"},
{file = "orjson-3.11.9-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:3ebca4179031ee716ed076ffadc29428e900512f6fccee8614c9983157fcf19c"},
{file = "orjson-3.11.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48ee05097750de0ff69ed5b7bbcf0732182fd57a24043dcc2a1da780a5ead3a5"},
{file = "orjson-3.11.9-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6082706765a95a6680d812e1daf1c0cfe8adec7831b3ff3b625693f3b461b1c"},
{file = "orjson-3.11.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:277fefe9d76ee17eb14debf399e3533d4d63b5f677a4d3719eb763536af1f4bd"},
{file = "orjson-3.11.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03db380e3780fa0015ed776a90f20e8e20bb11dde13b216ce19e5718e3dfba62"},
{file = "orjson-3.11.9-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33d7d766701847dc6729846362dc27895d2f2d2251264f9d10e7cb9878194877"},
{file = "orjson-3.11.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:147302878da387104b66bb4a8b0227d1d487e976ce41a8501916161072ed87b1"},
{file = "orjson-3.11.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3513550321f8c8c811a7c3297b8a630e82dc08e4c10216d07703c997776236cd"},
{file = "orjson-3.11.9-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c5d001196b89fa9cf0a4ab79766cd835b991a166e4b621ba95089edc50c429ff"},
{file = "orjson-3.11.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:16969c9d369c98eb084889c6e4d2d39b77c7eb38ceccf8da2a9fff62ae908980"},
{file = "orjson-3.11.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:63e0efbc991250c0b3143488fa57d95affcabbfc63c99c48d625dd37779aafe2"},
{file = "orjson-3.11.9-cp311-cp311-win32.whl", hash = "sha256:14ed654580c1ed2bc217352ec82f91b047aef82951aa71c7f64e0dcb03c0e180"},
{file = "orjson-3.11.9-cp311-cp311-win_amd64.whl", hash = "sha256:57ea77fb70a448ce87d18fca050193202a3da5e54598f6501ca5476fb66cfe02"},
{file = "orjson-3.11.9-cp311-cp311-win_arm64.whl", hash = "sha256:19b72ed11572a2ee51a67a903afbe5af504f84ed6f529c0fe44b0ab3fb5cc697"},
{file = "orjson-3.11.9-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9ef6fe90aadef185c7b128859f40beb24720b4ecea95379fc9000931179c3a49"},
{file = "orjson-3.11.9-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:e5c9b8f28e726e97d97696c826bc7bea5d71cecd63576dba92924a32c1961291"},
{file = "orjson-3.11.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a473dbb4162108b27901492546f83c76fdcea3d0eadff00ae7a07e18dcce09"},
{file = "orjson-3.11.9-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:011382e2a60fda9d46f1cdee31068cfc52ffe952b587d683ec0463002802a0f4"},
{file = "orjson-3.11.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c2d3dc759490128c5c1711a53eeaa8ee1d437fd0038ffd2b6008abf46db3f882"},
{file = "orjson-3.11.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8ea516b3726d190e1b4297e6f4e7a8650347ae053868a18163b4dd3641d1fff"},
{file = "orjson-3.11.9-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:380cdce7ba24989af81d0a7013d0aaec5d0e2a21734c0e2681b1bc4f141957fe"},
{file = "orjson-3.11.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be4fa4f0af7fa18951f7ab3fc2148e223af211bf03f59e1c6034ec3f97f21d61"},
{file = "orjson-3.11.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a8f5f8bc7ce7d59f08d9f99fa510c06496164a24cb5f3d34537dbd9ca30132e2"},
{file = "orjson-3.11.9-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:4d7fde5501b944f83b3e665e1b31343ff6e154b15560a16b7130ea1e594a4206"},
{file = "orjson-3.11.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cde1a448023ba7d5bb4c01c5afb48894380b5e4956e0627266526587ef4e535f"},
{file = "orjson-3.11.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:71e63adb0e1f1ed5d9e168f50a91ceb93ae6420731d222dc7da5c69409aa47aa"},
{file = "orjson-3.11.9-cp312-cp312-win32.whl", hash = "sha256:2d057a602cdd19a0ad680417527c45b6961a095081c0f46fe0e03e304aac6470"},
{file = "orjson-3.11.9-cp312-cp312-win_amd64.whl", hash = "sha256:59e403b1cc5a676da8eaf31f6254801b7341b3e29efa85f92b48d272637e77be"},
{file = "orjson-3.11.9-cp312-cp312-win_arm64.whl", hash = "sha256:9af678d6488357948f1f84c6cd1c1d397c014e1ae2f98ae082a44eb48f602624"},
{file = "orjson-3.11.9-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4bab1b2d6141fe7b32ae71dac905666ece4f94936efbfb13d55bb7739a3a6021"},
{file = "orjson-3.11.9-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:844417969855fc7a41be124aafe83dc424592a7f77cd4501900c67307122b92c"},
{file = "orjson-3.11.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffe02797b5e9f3a9d8292ddcd289b474ad13e81ad83cd1891a240811f1d2cb81"},
{file = "orjson-3.11.9-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e4eed3b200023042814d2fc8a5d2e880f13b52e1ed2485e83da4f3962f7dc1a"},
{file = "orjson-3.11.9-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aff7da9952a5ad1cef8e68017724d96c7b9a66e99e91d6252e1b133d67a7b10"},
{file = "orjson-3.11.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d4e98d6f3b8afed8bc8cd9718ec0cdf46661826beefb53fe8eafb37f2bf0362"},
{file = "orjson-3.11.9-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a81d52442a7c99b3662333235b3adf96a1715864658b35bb797212be7bddb97"},
{file = "orjson-3.11.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e39364e726a8fff737309aff059ff67d8a8c8d5b677be7bb49a8b3e84b7e218"},
{file = "orjson-3.11.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4fd66214623f1b17501df9f0543bef0b833979ab5b6ded1e1d123222866aa8c9"},
{file = "orjson-3.11.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8ecc30f10465fa1e0ce13fd01d9e22c316e5053a719a8d915d4545a09a5ff677"},
{file = "orjson-3.11.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:97db4c94a7db398a5bd636273324f0b3fd58b350bbbac8bb380ceb825a9b40f4"},
{file = "orjson-3.11.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9f78cf8fec5bd627f4082b8dfeac7871b43d7f3274904492a43dab39f18a19a0"},
{file = "orjson-3.11.9-cp313-cp313-win32.whl", hash = "sha256:d4087e5c0209a0a8efe4de3303c234b9c44d1174161dcd851e8eea07c7560b32"},
{file = "orjson-3.11.9-cp313-cp313-win_amd64.whl", hash = "sha256:051b102c93b4f634e89f3866b07b9a9a98915ada541f4ec30f177067b2694979"},
{file = "orjson-3.11.9-cp313-cp313-win_arm64.whl", hash = "sha256:cce9127885941bd28f080cecf1f1d288336b7e0d812c345b08be88b572796254"},
{file = "orjson-3.11.9-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b6ef1979adc4bc243523f1a2ba91418030a8e29b0a99cbe7e0e2d6807d4dce6e"},
{file = "orjson-3.11.9-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:f36b7f32c7c0db4a719f1fc5824db4a9c6f8bd1a354debb91faf26ebf3a4c71e"},
{file = "orjson-3.11.9-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08f4d8ebb44925c794e535b2bebc507cebf32209df81de22ae285fb0d8d66de0"},
{file = "orjson-3.11.9-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6cc7923789694fd58f001cbcac7e47abc13af4d560ebbfcf3b41a8b1a0748124"},
{file = "orjson-3.11.9-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea5c46eb2d3af39e806b986f4b09d5c2706a1f5afde3cbf7544ce6616127173c"},
{file = "orjson-3.11.9-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f5d89a2ed90731df3be64bab0aa44f78bff39fdc9d71c291f4a8023aa46425b7"},
{file = "orjson-3.11.9-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:25e4aed0312d292c09f61af25bba34e0b2c88546041472b09088c39a4d828af1"},
{file = "orjson-3.11.9-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaea64f3f467d22e70eeed68bdccb3bc4f83f650446c4a03c59f2cba28a108db"},
{file = "orjson-3.11.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a028425d1b440c5d92a6be1e1a020739dfe67ea87d96c6dbe828c1b30041728b"},
{file = "orjson-3.11.9-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5b192c6cf397e4455b11523c5cf2b18ed084c1bbd61b6c0926344d2129481972"},
{file = "orjson-3.11.9-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ea407d4ccf5891d667d045fecae97a7a1e5e87b3b97f97ae1803c2e741130be0"},
{file = "orjson-3.11.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5f63aaf97afd9f6dec5b1a68e1b8da12bfccb4cb9a9a65c3e0b6c847849e7586"},
{file = "orjson-3.11.9-cp314-cp314-win32.whl", hash = "sha256:e30ab17845bb9fa54ccf67fa4f9f5282652d54faa6d17452f47d0f369d038673"},
{file = "orjson-3.11.9-cp314-cp314-win_amd64.whl", hash = "sha256:32ef5f4283a3be81913947d19608eacb7c6608026851123790cd9cc8982af34b"},
{file = "orjson-3.11.9-cp314-cp314-win_arm64.whl", hash = "sha256:eebdbdeef0094e4f5aefa20dcd4eb2368ab5e7a3b4edea27f1e7b2892e009cf9"},
{file = "orjson-3.11.9.tar.gz", hash = "sha256:4fef17e1f8722c11587a6ef18e35902450221da0028e65dbaaa543619e68e48f"},
]
[[package]]
name = "packaging"
version = "26.2"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.8"
groups = ["main", "dev"]
files = [
{file = "packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e"},
{file = "packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661"},
]
markers = {main = "extra == \"docs\""}
[[package]]
name = "pathspec"
version = "1.1.1"
description = "Utility library for gitignore style pattern matching of file paths."
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189"},
{file = "pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a"},
]
[package.extras]
hyperscan = ["hyperscan (>=0.7)"]
optional = ["typing-extensions (>=4)"]
re2 = ["google-re2 (>=1.1)"]
[[package]]
name = "platformdirs"
version = "4.10.0"
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
optional = false
python-versions = ">=3.10"
groups = ["dev"]
files = [
{file = "platformdirs-4.10.0-py3-none-any.whl", hash = "sha256:fb516cdb12eb0d857d0cd85a7c57cea4d060bee4578d6cf5a14dfdf8cbf8784a"},
{file = "platformdirs-4.10.0.tar.gz", hash = "sha256:31e761a6a0ca04faf7353ea759bdba55652be214725111e5aac52dfa29d4bef7"},
]
[[package]]
name = "pluggy"
version = "1.6.0"
description = "plugin and hook calling mechanisms for python"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"},
{file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"},
]
[package.extras]
dev = ["pre-commit", "tox"]
testing = ["coverage", "pytest", "pytest-benchmark"]
[[package]]
name = "propcache"
version = "0.5.2"
description = "Accelerated property cache"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "propcache-0.5.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a81be28596d6559f6131ef33e10200de6e17643b3c74ce03f9eb103be6ae8b"},
{file = "propcache-0.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29cbaac5ea0212663e6845e04b5e188d5a6ae6dd919810ac835bf1d3b42c3f4c"},
{file = "propcache-0.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6bf3be92233808fcd338eba0fb4d0b59ec5772af4f4ecfcec450d1bfc0f8b5eb"},
{file = "propcache-0.5.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2f8ea531c794b9d6274acd4e8d2c2ebcac590a4361d27482edd3010b79f1325e"},
{file = "propcache-0.5.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:decfca4c79dd53ebab484b00cc4b6717d8c369f86e74aa4ca395a64ac651495e"},
{file = "propcache-0.5.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4621064bbf28fa77ff64dd5d94367c04684c67d3a5bf1dff25f0cd0d98a38f3b"},
{file = "propcache-0.5.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b96db7141a592cbc968daf1feea83a118e6ab378af4abbc72b248c895414c22d"},
{file = "propcache-0.5.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1ca071adabaab6e9219924bbe00af821f1ee7de113a9eca1cdc292de3d120f4d"},
{file = "propcache-0.5.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e4294d04a94dcab1b3bccd8b66d962dcad411a1d19414b2a41d1445f1de32ad0"},
{file = "propcache-0.5.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a0e399a2eccb91ed18721f86aa85757727400b6865c89e88934781deb9c8498b"},
{file = "propcache-0.5.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:823581fd5cb08b12a48bfa11fe962a7916766b6170c17b028fbdf762b85eb9bf"},
{file = "propcache-0.5.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:949c91d1a990cf3b2e8188dfcfb25005e0b834a06c63fa4ef9f360878ce21ecf"},
{file = "propcache-0.5.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:cc1177027eda740fdb152706bd215a3f124e3eea15afc39f2cb9fe351b50619e"},
{file = "propcache-0.5.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b05d643f944a8c3c4bd86d65ffd87bf3264b617f87791940302bc474d2ff5274"},
{file = "propcache-0.5.2-cp310-cp310-win32.whl", hash = "sha256:8114f28879e0904748e831c3a7774261bd9e75f49be089f389a76f959dcd13fe"},
{file = "propcache-0.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:5fcb98e7598b1ee0addab320d90f65b530297a867dbfe9de52ea838077e16e3d"},
{file = "propcache-0.5.2-cp310-cp310-win_arm64.whl", hash = "sha256:04dc2390d9edbbaef7461f33322555976ffddf0b650a038649d026358714e6c5"},
{file = "propcache-0.5.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74b70780220e2dd89175ca24b81b68b67c83db499ae611e7f2313cb329801c78"},
{file = "propcache-0.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4840ab0ae0216d952f4b53dc6d0b992bfc2bedbfe360bdd9b548bc184c08959"},
{file = "propcache-0.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c6844ba6364fb12f403928a82cfd295ab103a2b315c77c747b2dbe4a41894ea7"},
{file = "propcache-0.5.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2293949b855ce597f2826452d17c2d545fb5622379c4ea6fdf525e9b8e8a2511"},
{file = "propcache-0.5.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0fd59b5af35f74da48d905dcbad55449ba13be91823cb05a9bd590bbf5b61660"},
{file = "propcache-0.5.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29f9309a2e42b0d273be006fdb4be2d6c39a47f6f57d8fb1cf9f81481df81b66"},
{file = "propcache-0.5.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5aaa2b923c1944ac8febd6609cb373540a5563e7cbcb0fd770f75dace2eb817b"},
{file = "propcache-0.5.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66ea454f095ddf5b6b14f56c064c0941c4788be11e18d2464cf643bf7203ff67"},
{file = "propcache-0.5.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:95f1e3f4760d404b13c9976c0229b2b49a3c8e2c62a9ce92efdd2b11ada75e3f"},
{file = "propcache-0.5.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:85341b12b9d55bad0bded24cac341bb34289469e03a11f3f583ea1cc1db0326c"},
{file = "propcache-0.5.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:26a4dca084132874e639895c3135dfad5eb20bae209f62d1aeb31b03e601c3c0"},
{file = "propcache-0.5.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3b199b9b2b3d6a7edf3183ba8a9a137a22b97f7df525feb5ae1eccf026d2a9c6"},
{file = "propcache-0.5.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e59bc9e66329185b93dab73f210f1a37f81cb40f321501db8017c9aea15dba27"},
{file = "propcache-0.5.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:552ffadf6ad409844bc5919c42a0a83d88314cedddaea0e41e80a8b8fffe881f"},
{file = "propcache-0.5.2-cp311-cp311-win32.whl", hash = "sha256:cd416c1de191973c52ff1a12a57446bfc7642797b282d7caf2162d7d1b8aa9a0"},
{file = "propcache-0.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:44e488ef40dbb452700b2b1f8188934121f6648f52c295055662d2191959ff82"},
{file = "propcache-0.5.2-cp311-cp311-win_arm64.whl", hash = "sha256:54adaa85a22078d1e306304a40984dc5be99d599bf3dc0a24dc98f7daeab89ab"},
{file = "propcache-0.5.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:806719138ecd720339a12410fb9614ac9b2b2d3a5fdf8235d56981c36f4039ba"},
{file = "propcache-0.5.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:db2b80ea58eab4f86b2beec3cc8b39e8ff9276ac20e96b7cce43c8ae84cd6b5a"},
{file = "propcache-0.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e5cbfac9f61484f7e9f3597775500cd3ebe8274e9b050c38f9525c77c97520bf"},
{file = "propcache-0.5.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbc581d2814337da56222fab8dc5f161cd798a434e49bac27930aaef798e144"},
{file = "propcache-0.5.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:857187f381f88c8e2fa2fe56ab94879d011b883d5a2ee5a1b60a8cd2a06846d9"},
{file = "propcache-0.5.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:178b4a2cdaac1818e2bf1c5a99b94383fa73ea5382e032a48dec07dc5668dc42"},
{file = "propcache-0.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f328175a2cde1f0ff2c4ed8ce968b9dcfb55f3a7153f39e2957ed994da13476"},
{file = "propcache-0.5.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5671d09a36b06d0fd4a3da0fccbcae360e9b1570924171a15e9e0997f0249fba"},
{file = "propcache-0.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80168e2ebe4d3ec6599d10ad8f520304ae1cad9b6c5a95372aef1b66b7bfb53a"},
{file = "propcache-0.5.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:45f11346f884bc47444f6e6647131055844134c3175b629f84952e2b5cd62b64"},
{file = "propcache-0.5.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e778ebd44ef4f66ed60a0416b06b489687db264a9c0b3620362f26489492913"},
{file = "propcache-0.5.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:c0cb9ed24c8964e172768d455a38254c2dd8a552905729ce006cad3d3dda59b1"},
{file = "propcache-0.5.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1d1ad32d9d4355e2be65574fd0bfd3677e7066b009cd5b9b2dee8aa6a6393b33"},
{file = "propcache-0.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c80f4ba3e8f00189165999a742ee526ebeccedf6c3f7beb0c7df821e9772435a"},
{file = "propcache-0.5.2-cp312-cp312-win32.whl", hash = "sha256:8c7972d8f193740d9175f0998ab38717e6cd322d5935c5b0fef8c0d323fd9031"},
{file = "propcache-0.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:d9ee8826a7d47863a08ac44e1a5f611a462eefc3a194b492da242128bec75b42"},
{file = "propcache-0.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:2800a4a8ead6b28cccd1ec54b59346f0def7922ee1c7598e8499c733cfbb7c84"},
{file = "propcache-0.5.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:099aaf4b4d1a02265b92a977edf00b5c4f63b3b17ac6de39b0d637c9cac0188a"},
{file = "propcache-0.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68ce1c44c7a813a7f71ea04315a8c7b330b63db99d059a797a4651bb6f69f117"},
{file = "propcache-0.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fc299c129490f55f254cd90be0deca4764e36e9a7c08b4aa588479a3bbed3098"},
{file = "propcache-0.5.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6ae2198be502c10f09b2516e7b5d019816924bc3183a43ce792a7bd6625e6f4"},
{file = "propcache-0.5.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6041d31504dc1779d700e1edcfb08eea334b357620b06681a4eabb57a74e574e"},
{file = "propcache-0.5.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7eabc04151c78a9f4d5bbb5f1faf571e4defeb4b585e0fe95b60ff2dbe4d3d7"},
{file = "propcache-0.5.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4db0ba63d693afd40d249bd93f842b5f144f8fcbb83de05660373bcf30517b1d"},
{file = "propcache-0.5.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1dbcf7675229b35d31abb6547d8ebc8c27a830ac3f9a794edff6254873ec7c0a"},
{file = "propcache-0.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d310c013aad2c72f1c3f2f8dd3279d460a858c551f97aeb8c63e4693cca7b4d2"},
{file = "propcache-0.5.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:06187263ddad280d05b4d8a8b3bb7d164cbebd469236544a42e6d9b28ac6a4fa"},
{file = "propcache-0.5.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3115559b8effafd63b142ea5ed53d63a16ea6469cbc63dce4ee194b42db5d853"},
{file = "propcache-0.5.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c60462af8e6dc30c35407c7237ea908d777b22862bbee27bc4699c0d8bcdc45a"},
{file = "propcache-0.5.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40314bca9ac559716fe374094fc81c11dcc34b64fd6c585360f5775690505704"},
{file = "propcache-0.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cfa21e036ce1e1db2be04ba3b85d2df1bb1702fa01932d984c5464c665228ff4"},
{file = "propcache-0.5.2-cp313-cp313-win32.whl", hash = "sha256:f156a3529f38063b6dbaf356e15602a7f95f8055b1295a438433a6386f10463d"},
{file = "propcache-0.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:dfed59d0a5aeb01e242e66ff0300bc4a265a7c05f612d30016f0b60b1017d757"},
{file = "propcache-0.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:ba338430e87ceb9c8f0cf754de38a9860560261e56c00376debd628698a7364f"},
{file = "propcache-0.5.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a592f5f3da71c8691c788c13cb6734b6d17663d2e1cb8caddf0673d01ef8847d"},
{file = "propcache-0.5.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6a997d0489e9668a384fcfd5061b857aa5361de73191cac204d04b889cfbbafa"},
{file = "propcache-0.5.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:10734b5484ea113152ee25a91dccedf81631791805d2c9ccb054958e51842c94"},
{file = "propcache-0.5.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cafca7e56c12bb02ae16d283742bef25a61122e9dab2b5b3f2ccbe589ce32164"},
{file = "propcache-0.5.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f064f8d2b59177878b7615df1735cd8fe3462ed6be8c7b217d17a276489c2b7f"},
{file = "propcache-0.5.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f78abfa8dfc32376fd1aacf597b2f2fbbe0ea751419aee718af5d4f82537ef8c"},
{file = "propcache-0.5.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7467da8a9822bf1a55336f877340c5bcbd3c482afc43a99771169f74a26dedc"},
{file = "propcache-0.5.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a6ddc6ac9e25de626c1f129c1b467d7ecd33ce2237d3fd0c4e429feef0a7ee1f"},
{file = "propcache-0.5.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2f22cbbac9e26a8e864c0985ff1268d5d939d53d9d9411a9824279097e03a2cb"},
{file = "propcache-0.5.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:fc76378c62a0f04d0cd82fbb1a2cd2d7e28fcb40d5873f28a6c44e388aaa2751"},
{file = "propcache-0.5.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:acd2c8edba48e31e58a363b8cf4e5c7db3b04b3f9e371f601df30d9b0d244836"},
{file = "propcache-0.5.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:452b5065457eb9991ec5eb38ff41d6cd4c991c9ac7c531c4d5849ae473a9a13f"},
{file = "propcache-0.5.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:3430bb2bfe1331885c427745a751e774ee679fd4344f80b97bf879815fe8fa55"},
{file = "propcache-0.5.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cef6cea3922890dd6c9654971001fa797b526c16ab5e1e46c05fd6f877be7568"},
{file = "propcache-0.5.2-cp313-cp313t-win32.whl", hash = "sha256:72d61e16dd78228b58c5d47be830ff3da7e5f139abdf0aef9d86cde1c5cf2191"},
{file = "propcache-0.5.2-cp313-cp313t-win_amd64.whl", hash = "sha256:0958834041a0166d343b8d2cedcd8bcbaeb4fdbe0cf08320c5379f143c3be6e7"},
{file = "propcache-0.5.2-cp313-cp313t-win_arm64.whl", hash = "sha256:6de8bd93ddde9b992cf2b2e0d796d501a19026b5b9fd87356d7d0779531a8d96"},
{file = "propcache-0.5.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:46088abff4cba581dea21ae0467a480526cb25aa5f3c269e909f800328bc3999"},
{file = "propcache-0.5.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fc88b26f08d634f7bc819a7852e5214f5802641ab8d9fd5326892292eee1993e"},
{file = "propcache-0.5.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97797ebb098e670a2f92dd66f32897e30d7615b14e7f59711de23e30a9072539"},
{file = "propcache-0.5.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba57fffe4ac99c5d30076161b5866336d97600769bad35cc68f7774b15298a4e"},
{file = "propcache-0.5.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:583c19759d9eec1e5b69e2fbef36a7d9c326041be9746cb822d335c8cedc2979"},
{file = "propcache-0.5.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d0326e2e5e1f3163fa306c834e48e8d490e5fae607a097a40c0648109b47ba80"},
{file = "propcache-0.5.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e00820e192c8dbebcafb383ebbf99030895f09905e7a0eb2e0340a0bcc2bc825"},
{file = "propcache-0.5.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c66afea89b1e43725731d2004732a046fe6fe955d51f952c3e95a7314a284a39"},
{file = "propcache-0.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc37dec6c6cdad0b57881a5658fd14fbf53e333b1a86cf86559f190e1d9ec4"},
{file = "propcache-0.5.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5570dbcc97571c15f68068e529c92715a12f8d54030e272d264b377e22bd17a5"},
{file = "propcache-0.5.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f814362777a9f841adddb200ecdf8f5cb1e5a3c4b7a86378edbd6ccb26edd702"},
{file = "propcache-0.5.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:196913dea116aeb5a2ba95af4ddcb7ea85559ae07d8eee8751688310d09168c3"},
{file = "propcache-0.5.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6e7b8719005dd1175be4ab1cd25e9b98659a5e0347331506ec6760d2773a7fb5"},
{file = "propcache-0.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:51f96d685ab16e88cab128cd37a52c5da540809c8b879fa047731bfcb4ad35a4"},
{file = "propcache-0.5.2-cp314-cp314-win32.whl", hash = "sha256:cc6fc3cc62e8501d3ed62894425040d2728ecddb1ed072737a5c70bd537aa9f0"},
{file = "propcache-0.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:81e3a30b0bb60caa22033dd0f8a3618d1d67356212514f62c57db75cb0ef410c"},
{file = "propcache-0.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:0d2c9bf8528f135dbb805ce027567e09164f7efa51a2be07458a2c0420f292d0"},
{file = "propcache-0.5.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:4bc8ff1feffc6a61c7002ffe84634c41b822e104990ae009f44a0834430070bb"},
{file = "propcache-0.5.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:79aa3ff0a9b566633b642fa9caf7e21ed1c13d6feca718187873f199e1514078"},
{file = "propcache-0.5.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1b31822f4474c4036bae62de9402710051d431a606d6a0f907fec79935a071aa"},
{file = "propcache-0.5.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13fef48778b5a2a756523fdb781326b028ca75e32858b04f2cdd19f394564917"},
{file = "propcache-0.5.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8b73ab70f1a3351fbc71f663b3e645af6dd0329100c353081cf69c37433fc6fe"},
{file = "propcache-0.5.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5538d2c13d93e4698af7e092b57bc7298fd35d1d58e656ae18f23ee0d0378e03"},
{file = "propcache-0.5.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd645f03898405cabe694fb8bc35241e3a9c332ec85627584fe3de201452b335"},
{file = "propcache-0.5.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a473b3440261e0c60706e732b2ed2f517857344fc21bf48fdfe211e2d98eb285"},
{file = "propcache-0.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7afa37062e6650640e932e4cc9297d81f9f42d9944029cc386b8247dea4da837"},
{file = "propcache-0.5.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:8a90efd5777e996e42d568db9ac740b944d691e565cbfd31b2f7832f9184b2b8"},
{file = "propcache-0.5.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f19bb891234d72535764d703bfed1153cc34f4214d5bd7150aee1eec9e8f4366"},
{file = "propcache-0.5.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:32775082acd2d807ee3db715c7770d38767b817870acfa08c29e057f3c4d5b56"},
{file = "propcache-0.5.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9282fb1a3bccd038da9f768b927b24a0c753e466c086b7c4f3c6982851eefb2d"},
{file = "propcache-0.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc49723e2f60d6b32a0f0b08a3fd6d13203c07f1cd9566cfce0f12a917c967a2"},
{file = "propcache-0.5.2-cp314-cp314t-win32.whl", hash = "sha256:2d7aa89ebca5acc98cba9d1472d976e394782f587bad6661003602a619fd1821"},
{file = "propcache-0.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:d447bb0b3054be5818458fbb171208b1d9ff11eba14e18ca18b90cbb45767370"},
{file = "propcache-0.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:fe67a3d11cd9b4efabfa45c3d00ffba2b26811442a73a581a94b67c2b5faccf6"},
{file = "propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe"},
{file = "propcache-0.5.2.tar.gz", hash = "sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427"},
]
[[package]]
name = "pycodestyle"
version = "2.14.0"
description = "Python style guide checker"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d"},
{file = "pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783"},
]
[[package]]
name = "pycparser"
version = "3.0"
description = "C parser in Python"
optional = false
python-versions = ">=3.10"
groups = ["main"]
markers = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\""
files = [
{file = "pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992"},
{file = "pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29"},
]
[[package]]
name = "pycryptodomex"
version = "3.23.0"
description = "Cryptographic library for Python"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
groups = ["main"]
files = [
{file = "pycryptodomex-3.23.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:add243d204e125f189819db65eed55e6b4713f70a7e9576c043178656529cec7"},
{file = "pycryptodomex-3.23.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1c6d919fc8429e5cb228ba8c0d4d03d202a560b421c14867a65f6042990adc8e"},
{file = "pycryptodomex-3.23.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:1c3a65ad441746b250d781910d26b7ed0a396733c6f2dbc3327bd7051ec8a541"},
{file = "pycryptodomex-3.23.0-cp27-cp27m-win32.whl", hash = "sha256:47f6d318fe864d02d5e59a20a18834819596c4ed1d3c917801b22b92b3ffa648"},
{file = "pycryptodomex-3.23.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:d9825410197a97685d6a1fa2a86196430b01877d64458a20e95d4fd00d739a08"},
{file = "pycryptodomex-3.23.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:267a3038f87a8565bd834317dbf053a02055915acf353bf42ededb9edaf72010"},
{file = "pycryptodomex-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:7b37e08e3871efe2187bc1fd9320cc81d87caf19816c648f24443483005ff886"},
{file = "pycryptodomex-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:91979028227543010d7b2ba2471cf1d1e398b3f183cb105ac584df0c36dac28d"},
{file = "pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b8962204c47464d5c1c4038abeadd4514a133b28748bcd9fa5b6d62e3cec6fa"},
{file = "pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a33986a0066860f7fcf7c7bd2bc804fa90e434183645595ae7b33d01f3c91ed8"},
{file = "pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7947ab8d589e3178da3d7cdeabe14f841b391e17046954f2fbcd941705762b5"},
{file = "pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c25e30a20e1b426e1f0fa00131c516f16e474204eee1139d1603e132acffc314"},
{file = "pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:da4fa650cef02db88c2b98acc5434461e027dce0ae8c22dd5a69013eaf510006"},
{file = "pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:58b851b9effd0d072d4ca2e4542bf2a4abcf13c82a29fd2c93ce27ee2a2e9462"},
{file = "pycryptodomex-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:a9d446e844f08299236780f2efa9898c818fe7e02f17263866b8550c7d5fb328"},
{file = "pycryptodomex-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:bc65bdd9fc8de7a35a74cab1c898cab391a4add33a8fe740bda00f5976ca4708"},
{file = "pycryptodomex-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:c885da45e70139464f082018ac527fdaad26f1657a99ee13eecdce0f0ca24ab4"},
{file = "pycryptodomex-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:06698f957fe1ab229a99ba2defeeae1c09af185baa909a31a5d1f9d42b1aaed6"},
{file = "pycryptodomex-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b2c2537863eccef2d41061e82a881dcabb04944c5c06c5aa7110b577cc487545"},
{file = "pycryptodomex-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43c446e2ba8df8889e0e16f02211c25b4934898384c1ec1ec04d7889c0333587"},
{file = "pycryptodomex-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f489c4765093fb60e2edafdf223397bc716491b2b69fe74367b70d6999257a5c"},
{file = "pycryptodomex-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdc69d0d3d989a1029df0eed67cc5e8e5d968f3724f4519bd03e0ec68df7543c"},
{file = "pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6bbcb1dd0f646484939e142462d9e532482bc74475cecf9c4903d4e1cd21f003"},
{file = "pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:8a4fcd42ccb04c31268d1efeecfccfd1249612b4de6374205376b8f280321744"},
{file = "pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:55ccbe27f049743a4caf4f4221b166560d3438d0b1e5ab929e07ae1702a4d6fd"},
{file = "pycryptodomex-3.23.0-cp37-abi3-win32.whl", hash = "sha256:189afbc87f0b9f158386bf051f720e20fa6145975f1e76369303d0f31d1a8d7c"},
{file = "pycryptodomex-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:52e5ca58c3a0b0bd5e100a9fbc8015059b05cffc6c66ce9d98b4b45e023443b9"},
{file = "pycryptodomex-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:02d87b80778c171445d67e23d1caef279bf4b25c3597050ccd2e13970b57fd51"},
{file = "pycryptodomex-3.23.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:febec69c0291efd056c65691b6d9a339f8b4bc43c6635b8699471248fe897fea"},
{file = "pycryptodomex-3.23.0-pp27-pypy_73-win32.whl", hash = "sha256:c84b239a1f4ec62e9c789aafe0543f0594f0acd90c8d9e15bcece3efe55eca66"},
{file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ebfff755c360d674306e5891c564a274a47953562b42fb74a5c25b8fc1fb1cb5"},
{file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eca54f4bb349d45afc17e3011ed4264ef1cc9e266699874cdd1349c504e64798"},
{file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2596e643d4365e14d0879dc5aafe6355616c61c2176009270f3048f6d9a61f"},
{file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fdfac7cda115bca3a5abb2f9e43bc2fb66c2b65ab074913643803ca7083a79ea"},
{file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:14c37aaece158d0ace436f76a7bb19093db3b4deade9797abfc39ec6cd6cc2fe"},
{file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7de1e40a41a5d7f1ac42b6569b10bcdded34339950945948529067d8426d2785"},
{file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bffc92138d75664b6d543984db7893a628559b9e78658563b0395e2a5fb47ed9"},
{file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df027262368334552db2c0ce39706b3fb32022d1dce34673d0f9422df004b96a"},
{file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e79f1aaff5a3a374e92eb462fa9e598585452135012e2945f96874ca6eeb1ff"},
{file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:27e13c80ac9a0a1d050ef0a7e0a18cc04c8850101ec891815b6c5a0375e8a245"},
{file = "pycryptodomex-3.23.0.tar.gz", hash = "sha256:71909758f010c82bc99b0abf4ea12012c98962fbf0583c2164f8b84533c2e4da"},
]
[[package]]
name = "pyflakes"
version = "3.4.0"
description = "passive checker of Python programs"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f"},
{file = "pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58"},
]
[[package]]
name = "pygments"
version = "2.20.0"
description = "Pygments is a syntax highlighting package written in Python."
optional = false
python-versions = ">=3.9"
groups = ["main", "dev"]
files = [
{file = "pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176"},
{file = "pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f"},
]
markers = {main = "extra == \"docs\""}
[package.extras]
windows-terminal = ["colorama (>=0.4.6)"]
[[package]]
name = "pyobjc-core"
version = "12.2"
description = "Python<->ObjC Interoperability Module"
optional = false
python-versions = ">=3.10"
groups = ["main"]
markers = "sys_platform == \"darwin\""
files = [
{file = "pyobjc_core-12.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:45fffb2b8dc0ae4480386429bd1e7fa8aabeb2eec81816d6971b33936dfcfff8"},
{file = "pyobjc_core-12.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:17b4e003af384d3086c2f39989a0b282c5755dd1066f331cf7c9fa65febe22ce"},
{file = "pyobjc_core-12.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7677ed758a367bbbb5589d6f5276fb360a45c89168276c26162f61840b0fa03d"},
{file = "pyobjc_core-12.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9287c7c46d6ae8676b4c6c0389a8f4b5381f42ae53a47151900c08b157e5a992"},
{file = "pyobjc_core-12.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:515ecf2afe168301feb66a7230d700584ce2e4b8a0ac178e19450b8898384139"},
{file = "pyobjc_core-12.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:a51352e478785cd7fce1604b9902125a286139caea0759cb340e59d75b594992"},
{file = "pyobjc_core-12.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:3137b2d14f9f2154fb5b1c092c38d15e164f68ab190c18335d76e4e7e1583f79"},
{file = "pyobjc_core-12.2-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:1e4216f2ec962dc13cf7f31b9bc3a7190337a0f401e7dc9de6b2d8c08b9dbb7a"},
{file = "pyobjc_core-12.2-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:ac952bac8057dd0b97ee7b311c39f97cad7430b7cfbd67ca0a30135a7d17d2ab"},
{file = "pyobjc_core-12.2.tar.gz", hash = "sha256:51d7de4cfa32f508c6a7aac31f131b12d5e196a8dcf588e6e8d7e6337224f66d"},
]
[[package]]
name = "pyobjc-framework-cocoa"
version = "12.2"
description = "Wrappers for the Cocoa frameworks on macOS"
optional = false
python-versions = ">=3.10"
groups = ["main"]
markers = "sys_platform == \"darwin\""
files = [
{file = "pyobjc_framework_cocoa-12.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a2efad455ded551d1a033fd22245d57b71b6c065e8b52158dceed1611b5ae033"},
{file = "pyobjc_framework_cocoa-12.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0bb7cd468ca89bbc2d1d8acb930b4f4fdce8826de30a66608fc28f8880c1f6a6"},
{file = "pyobjc_framework_cocoa-12.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:06cb92d97d1af9d1f459ae6cf1d1a7b824c12d3aff1b709885966acd6b7208c2"},
{file = "pyobjc_framework_cocoa-12.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f0bbe0abedfb24b11ff6c71e26cdefb0df001c6482f95591fad40c2688c16498"},
{file = "pyobjc_framework_cocoa-12.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:46b6681e2b21b099ed095339c140f2c8137d6ac5658653166ee90722f9e3c621"},
{file = "pyobjc_framework_cocoa-12.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:aecfd44908fa12a9291fb6ca2458ebbc611102de6784f2202a35fd5ed9f56c60"},
{file = "pyobjc_framework_cocoa-12.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ef679740f541c52118149b558b757d1f11d9dcf30c2a23344b13a6af6a99a1ab"},
{file = "pyobjc_framework_cocoa-12.2-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:290e544a8c2d0786a34a359d825eaad44ebaaa3b30cdd765b2755422ca39a0d2"},
{file = "pyobjc_framework_cocoa-12.2-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:5a0bed77b0b56074cc2b4564aae3e6e9d5da5fdf93252f50b6dbced0f7fece3a"},
{file = "pyobjc_framework_cocoa-12.2.tar.gz", hash = "sha256:20b392e2b7241caad0538dfde12143343e5dfe48f72e7df660a7548e635903dc"},
]
[package.dependencies]
pyobjc-core = ">=12.2"
[[package]]
name = "pyobjc-framework-corebluetooth"
version = "12.2"
description = "Wrappers for the framework CoreBluetooth on macOS"
optional = false
python-versions = ">=3.10"
groups = ["main"]
markers = "sys_platform == \"darwin\""
files = [
{file = "pyobjc_framework_corebluetooth-12.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c2732cd05d26ac1541ce41cc6ece4d94108601ed503ddfe6dc6afd36a834cd65"},
{file = "pyobjc_framework_corebluetooth-12.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b03beace02ca1963697f35faf4d3d0263f707e0182f67950212c890dd9d19e6f"},
{file = "pyobjc_framework_corebluetooth-12.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31da3686ea55e9f7f8fc321fe2fcdf23827acd24c91ae1798e47e1f3a8355ab7"},
{file = "pyobjc_framework_corebluetooth-12.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:b43a21eb9388453c867ce82bf5d475cfce4fe2ceaea974474071f1cfd178f40f"},
{file = "pyobjc_framework_corebluetooth-12.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:162623f74c85041bb8a2c9a8862cee6674531d603ad7a8d65a3efc81619f84c5"},
{file = "pyobjc_framework_corebluetooth-12.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8a3b35a1253de626d5efe705dda36a4f1fb8753e48aafdc4dbf99dc9c9526d86"},
{file = "pyobjc_framework_corebluetooth-12.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:bd0b9e7bc2fffcfa4843d7f103e1e927ce37817a70932d080378e1e29a073381"},
{file = "pyobjc_framework_corebluetooth-12.2-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:a2dd4bd8fde13b97a3339a42d6703c9c9b148663e2e23a184ffe2877c8e51470"},
{file = "pyobjc_framework_corebluetooth-12.2-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:42db087cd3a328abc620c76c7f708d7c2b451f3dbc0779983812a0c84320fe60"},
{file = "pyobjc_framework_corebluetooth-12.2.tar.gz", hash = "sha256:8b4e5ca99953c360c391a695b0782a5328fcecafd56fdf790ad709e932feb306"},
]
[package.dependencies]
pyobjc-core = ">=12.2"
pyobjc-framework-Cocoa = ">=12.2"
[[package]]
name = "pyobjc-framework-libdispatch"
version = "12.2"
description = "Wrappers for libdispatch on macOS"
optional = false
python-versions = ">=3.10"
groups = ["main"]
markers = "sys_platform == \"darwin\""
files = [
{file = "pyobjc_framework_libdispatch-12.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0a43de8025ef9e2db597dbe3d2b49c9952d0b0aa93a78d89b0bfdc58f851f42c"},
{file = "pyobjc_framework_libdispatch-12.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:38fb417f3f3ee1ec20e14821f150210f5b6122e5c1d8b04958bddb660ce3d04f"},
{file = "pyobjc_framework_libdispatch-12.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9fb0028b400661b6b2279e2b611d396a4217bc36aa9dadd3ae21419551f5e091"},
{file = "pyobjc_framework_libdispatch-12.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:265fe4edfd60de111c870932ab8397f845c314332a935aa7a27100fd8562cebc"},
{file = "pyobjc_framework_libdispatch-12.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:117ed20ca54e334f657e60b9b9a3de3741d91875fd50ee51ac6a09224912cc68"},
{file = "pyobjc_framework_libdispatch-12.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fef3158dd4a068a9db8108a5cbf7da385788136e1d8af2e6fb7f82215016ecb2"},
{file = "pyobjc_framework_libdispatch-12.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:02dc66f1c11e25eceecf3c1b2b397f1727659dcac3e0c7b97784d5af3cfab491"},
{file = "pyobjc_framework_libdispatch-12.2-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:a81d9329fdf5094c9bc09b916e1d2fb86ab27138161d3e7ddb76ec2e780a1aba"},
{file = "pyobjc_framework_libdispatch-12.2-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:8daaf8844b5b328cf8adb6e8c085bf2aad22237956f7c5dc6fbb7a46c5015af0"},
{file = "pyobjc_framework_libdispatch-12.2.tar.gz", hash = "sha256:4a41879ef7716b73d70f2e40ff39353d686cbc59d48c93217ed362d2b2baf1ba"},
]
[package.dependencies]
pyobjc-core = ">=12.2"
pyobjc-framework-Cocoa = ">=12.2"
[[package]]
name = "pyric"
version = "0.1.6.3"
description = "Python Wireless Library"
optional = false
python-versions = "*"
groups = ["main"]
files = [
{file = "PyRIC-0.1.6.3.tar.gz", hash = "sha256:b539b01cafebd2406c00097f94525ea0f8ecd1dd92f7731f43eac0ef16c2ccc9"},
]
[[package]]
name = "pytest"
version = "9.1.1"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.10"
groups = ["dev"]
files = [
{file = "pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c"},
{file = "pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313"},
]
[package.dependencies]
colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""}
iniconfig = ">=1.0.1"
packaging = ">=22"
pluggy = ">=1.5,<2"
pygments = ">=2.7.2"
[package.extras]
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"]
[[package]]
name = "pytest-cov"
version = "7.1.0"
description = "Pytest plugin for measuring coverage."
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678"},
{file = "pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2"},
]
[package.dependencies]
coverage = {version = ">=7.10.6", extras = ["toml"]}
pluggy = ">=1.2"
pytest = ">=7"
[package.extras]
testing = ["process-tests", "pytest-xdist", "virtualenv"]
[[package]]
name = "pytokens"
version = "0.4.1"
description = "A Fast, spec compliant Python 3.14+ tokenizer that runs on older Pythons."
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "pytokens-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a44ed93ea23415c54f3face3b65ef2b844d96aeb3455b8a69b3df6beab6acc5"},
{file = "pytokens-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:add8bf86b71a5d9fb5b89f023a80b791e04fba57960aa790cc6125f7f1d39dfe"},
{file = "pytokens-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:670d286910b531c7b7e3c0b453fd8156f250adb140146d234a82219459b9640c"},
{file = "pytokens-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4e691d7f5186bd2842c14813f79f8884bb03f5995f0575272009982c5ac6c0f7"},
{file = "pytokens-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:27b83ad28825978742beef057bfe406ad6ed524b2d28c252c5de7b4a6dd48fa2"},
{file = "pytokens-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440"},
{file = "pytokens-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a58d057208cb9075c144950d789511220b07636dd2e4708d5645d24de666bdc"},
{file = "pytokens-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b49750419d300e2b5a3813cf229d4e5a4c728dae470bcc89867a9ad6f25a722d"},
{file = "pytokens-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9907d61f15bf7261d7e775bd5d7ee4d2930e04424bab1972591918497623a16"},
{file = "pytokens-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee44d0f85b803321710f9239f335aafe16553b39106384cef8e6de40cb4ef2f6"},
{file = "pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083"},
{file = "pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1"},
{file = "pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1"},
{file = "pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9"},
{file = "pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68"},
{file = "pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b"},
{file = "pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f"},
{file = "pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1"},
{file = "pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4"},
{file = "pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78"},
{file = "pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321"},
{file = "pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa"},
{file = "pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d"},
{file = "pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324"},
{file = "pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9"},
{file = "pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb"},
{file = "pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3"},
{file = "pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975"},
{file = "pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a"},
{file = "pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918"},
{file = "pytokens-0.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:da5baeaf7116dced9c6bb76dc31ba04a2dc3695f3d9f74741d7910122b456edc"},
{file = "pytokens-0.4.1-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11edda0942da80ff58c4408407616a310adecae1ddd22eef8c692fe266fa5009"},
{file = "pytokens-0.4.1-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0fc71786e629cef478cbf29d7ea1923299181d0699dbe7c3c0f4a583811d9fc1"},
{file = "pytokens-0.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dcafc12c30dbaf1e2af0490978352e0c4041a7cde31f4f81435c2a5e8b9cabb6"},
{file = "pytokens-0.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:42f144f3aafa5d92bad964d471a581651e28b24434d184871bd02e3a0d956037"},
{file = "pytokens-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:34bcc734bd2f2d5fe3b34e7b3c0116bfb2397f2d9666139988e7a3eb5f7400e3"},
{file = "pytokens-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:941d4343bf27b605e9213b26bfa1c4bf197c9c599a9627eb7305b0defcfe40c1"},
{file = "pytokens-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3ad72b851e781478366288743198101e5eb34a414f1d5627cdd585ca3b25f1db"},
{file = "pytokens-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:682fa37ff4d8e95f7df6fe6fe6a431e8ed8e788023c6bcc0f0880a12eab80ad1"},
{file = "pytokens-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:30f51edd9bb7f85c748979384165601d028b84f7bd13fe14d3e065304093916a"},
{file = "pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de"},
{file = "pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a"},
]
[package.extras]
dev = ["black", "build", "mypy", "pytest", "pytest-cov", "setuptools", "tox", "twine", "wheel"]
[[package]]
name = "pyupgrade"
version = "3.21.2"
description = "A tool to automatically upgrade syntax for newer versions."
optional = false
python-versions = ">=3.10"
groups = ["dev"]
files = [
{file = "pyupgrade-3.21.2-py2.py3-none-any.whl", hash = "sha256:2ac7b95cbd176475041e4dfe8ef81298bd4654a244f957167bd68af37d52be9f"},
{file = "pyupgrade-3.21.2.tar.gz", hash = "sha256:1a361bea39deda78d1460f65d9dd548d3a36ff8171d2482298539b9dc11c9c06"},
]
[package.dependencies]
tokenize-rt = ">=6.1.0"
[[package]]
name = "pyyaml"
version = "6.0.3"
description = "YAML parser and emitter for Python"
optional = true
python-versions = ">=3.8"
groups = ["main"]
markers = "extra == \"docs\""
files = [
{file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"},
{file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"},
{file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"},
{file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"},
{file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"},
{file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"},
{file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"},
{file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"},
{file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"},
{file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"},
{file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"},
{file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"},
{file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"},
{file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"},
{file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"},
{file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"},
{file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"},
{file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"},
{file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"},
{file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"},
{file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"},
{file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"},
{file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"},
{file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"},
{file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"},
{file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"},
{file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"},
{file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"},
{file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"},
{file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"},
{file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"},
{file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"},
{file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"},
{file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"},
{file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"},
{file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"},
{file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"},
{file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"},
{file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"},
{file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"},
{file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"},
{file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"},
{file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"},
{file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"},
{file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"},
{file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"},
{file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"},
{file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"},
{file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"},
{file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"},
{file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"},
{file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"},
{file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"},
{file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"},
{file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"},
{file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"},
{file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"},
{file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"},
{file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"},
{file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"},
{file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"},
{file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"},
{file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"},
{file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"},
{file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"},
{file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"},
{file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"},
{file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"},
{file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"},
{file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"},
{file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"},
{file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"},
{file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"},
]
[[package]]
name = "requests"
version = "2.34.2"
description = "Python HTTP for Humans."
optional = true
python-versions = ">=3.10"
groups = ["main"]
markers = "extra == \"docs\""
files = [
{file = "requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0"},
{file = "requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed"},
]
[package.dependencies]
certifi = ">=2023.5.7"
charset_normalizer = ">=2,<4"
idna = ">=2.5,<4"
urllib3 = ">=1.26,<3"
[package.extras]
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<8)"]
[[package]]
name = "roman-numerals"
version = "4.1.0"
description = "Manipulate well-formed Roman numerals"
optional = true
python-versions = ">=3.10"
groups = ["main"]
markers = "extra == \"docs\""
files = [
{file = "roman_numerals-4.1.0-py3-none-any.whl", hash = "sha256:647ba99caddc2cc1e55a51e4360689115551bf4476d90e8162cf8c345fe233c7"},
{file = "roman_numerals-4.1.0.tar.gz", hash = "sha256:1af8b147eb1405d5839e78aeb93131690495fe9da5c91856cb33ad55a7f1e5b2"},
]
[[package]]
name = "sensor-state-data"
version = "2.20.0"
description = "Models for storing and converting Sensor Data state"
optional = false
python-versions = "<4.0,>=3.9"
groups = ["main"]
files = [
{file = "sensor_state_data-2.20.0-py3-none-any.whl", hash = "sha256:34aaff3c956fb832dd0c6bfd4c20f2152d0fc1f50b782951d958bf08f099c12c"},
{file = "sensor_state_data-2.20.0.tar.gz", hash = "sha256:bad940f9a36bc18b03f59a8245af87c19436240e9ca321afa87655c61a995ac0"},
]
[package.extras]
docs = ["Sphinx (>=5,<8)", "myst-parser (>=0.18,<3.1)", "sphinx-rtd-theme (>=1,<4)"]
[[package]]
name = "snowballstemmer"
version = "3.1.1"
description = "This package provides 36 stemmers for 34 languages generated from Snowball algorithms."
optional = true
python-versions = ">=3.3"
groups = ["main"]
markers = "extra == \"docs\""
files = [
{file = "snowballstemmer-3.1.1-py3-none-any.whl", hash = "sha256:7e207fa178741da09cdee59d3ecec3827ad5f92b1fc5c9ff3755b639f71f5752"},
{file = "snowballstemmer-3.1.1.tar.gz", hash = "sha256:e07bbc54a0d798fe6010a12398422e62a8bfbba95c394fd0956ef58cb4d3e260"},
]
[[package]]
name = "sphinx"
version = "9.0.4"
description = "Python documentation generator"
optional = true
python-versions = ">=3.11"
groups = ["main"]
markers = "extra == \"docs\""
files = [
{file = "sphinx-9.0.4-py3-none-any.whl", hash = "sha256:5bebc595a5e943ea248b99c13814c1c5e10b3ece718976824ffa7959ff95fffb"},
{file = "sphinx-9.0.4.tar.gz", hash = "sha256:594ef59d042972abbc581d8baa577404abe4e6c3b04ef61bd7fc2acbd51f3fa3"},
]
[package.dependencies]
alabaster = ">=0.7.14"
babel = ">=2.13"
colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\""}
docutils = ">=0.20,<0.23"
imagesize = ">=1.3"
Jinja2 = ">=3.1"
packaging = ">=23.0"
Pygments = ">=2.17"
requests = ">=2.30.0"
roman-numerals = ">=1.0.0"
snowballstemmer = ">=2.2"
sphinxcontrib-applehelp = ">=1.0.7"
sphinxcontrib-devhelp = ">=1.0.6"
sphinxcontrib-htmlhelp = ">=2.0.6"
sphinxcontrib-jsmath = ">=1.0.1"
sphinxcontrib-qthelp = ">=1.0.6"
sphinxcontrib-serializinghtml = ">=1.1.9"
[[package]]
name = "sphinx-rtd-theme"
version = "3.1.0"
description = "Read the Docs theme for Sphinx"
optional = true
python-versions = ">=3.8"
groups = ["main"]
markers = "extra == \"docs\""
files = [
{file = "sphinx_rtd_theme-3.1.0-py2.py3-none-any.whl", hash = "sha256:1785824ae8e6632060490f67cf3a72d404a85d2d9fc26bce3619944de5682b89"},
{file = "sphinx_rtd_theme-3.1.0.tar.gz", hash = "sha256:b44276f2c276e909239a4f6c955aa667aaafeb78597923b1c60babc76db78e4c"},
]
[package.dependencies]
docutils = ">0.18,<0.23"
sphinx = ">=6,<10"
sphinxcontrib-jquery = ">=4,<5"
[package.extras]
dev = ["bump2version", "transifex-client", "twine", "wheel"]
[[package]]
name = "sphinxcontrib-applehelp"
version = "2.0.0"
description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books"
optional = true
python-versions = ">=3.9"
groups = ["main"]
markers = "extra == \"docs\""
files = [
{file = "sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5"},
{file = "sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1"},
]
[package.extras]
lint = ["mypy", "ruff (==0.5.5)", "types-docutils"]
standalone = ["Sphinx (>=5)"]
test = ["pytest"]
[[package]]
name = "sphinxcontrib-devhelp"
version = "2.0.0"
description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents"
optional = true
python-versions = ">=3.9"
groups = ["main"]
markers = "extra == \"docs\""
files = [
{file = "sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2"},
{file = "sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad"},
]
[package.extras]
lint = ["mypy", "ruff (==0.5.5)", "types-docutils"]
standalone = ["Sphinx (>=5)"]
test = ["pytest"]
[[package]]
name = "sphinxcontrib-htmlhelp"
version = "2.1.0"
description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files"
optional = true
python-versions = ">=3.9"
groups = ["main"]
markers = "extra == \"docs\""
files = [
{file = "sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8"},
{file = "sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9"},
]
[package.extras]
lint = ["mypy", "ruff (==0.5.5)", "types-docutils"]
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"
groups = ["main"]
markers = "extra == \"docs\""
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"
groups = ["main"]
markers = "extra == \"docs\""
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 = "2.0.0"
description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents"
optional = true
python-versions = ">=3.9"
groups = ["main"]
markers = "extra == \"docs\""
files = [
{file = "sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb"},
{file = "sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab"},
]
[package.extras]
lint = ["mypy", "ruff (==0.5.5)", "types-docutils"]
standalone = ["Sphinx (>=5)"]
test = ["defusedxml (>=0.7.1)", "pytest"]
[[package]]
name = "sphinxcontrib-serializinghtml"
version = "2.0.0"
description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)"
optional = true
python-versions = ">=3.9"
groups = ["main"]
markers = "extra == \"docs\""
files = [
{file = "sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331"},
{file = "sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d"},
]
[package.extras]
lint = ["mypy", "ruff (==0.5.5)", "types-docutils"]
standalone = ["Sphinx (>=5)"]
test = ["pytest"]
[[package]]
name = "tokenize-rt"
version = "6.2.0"
description = "A wrapper around the stdlib `tokenize` which roundtrips."
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "tokenize_rt-6.2.0-py2.py3-none-any.whl", hash = "sha256:a152bf4f249c847a66497a4a95f63376ed68ac6abf092a2f7cfb29d044ecff44"},
{file = "tokenize_rt-6.2.0.tar.gz", hash = "sha256:8439c042b330c553fdbe1758e4a05c0ed460dbbbb24a606f11f0dee75da4cad6"},
]
[[package]]
name = "typing-extensions"
version = "4.15.0"
description = "Backported and Experimental Type Hints for Python 3.9+"
optional = false
python-versions = ">=3.9"
groups = ["main", "dev"]
files = [
{file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"},
{file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"},
]
markers = {main = "sys_platform == \"win32\" or python_version < \"3.13\""}
[[package]]
name = "uart-devices"
version = "0.1.1"
description = "UART Devices for Linux"
optional = false
python-versions = "<4.0,>=3.9"
groups = ["main"]
files = [
{file = "uart_devices-0.1.1-py3-none-any.whl", hash = "sha256:55bc8cce66465e90b298f0910e5c496bc7be021341c5455954cf61c6253dc123"},
{file = "uart_devices-0.1.1.tar.gz", hash = "sha256:3a52c4ae0f5f7400ebe1ae5f6e2a2d40cc0b7f18a50e895236535c4e53c6ed34"},
]
[[package]]
name = "urllib3"
version = "2.7.0"
description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = true
python-versions = ">=3.10"
groups = ["main"]
markers = "extra == \"docs\""
files = [
{file = "urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897"},
{file = "urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c"},
]
[package.extras]
brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""]
h2 = ["h2 (>=4,<5)"]
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""]
[[package]]
name = "usb-devices"
version = "0.5.1"
description = "Tools for mapping, describing, and resetting USB devices"
optional = false
python-versions = "<4.0,>=3.10"
groups = ["main"]
files = [
{file = "usb_devices-0.5.1-py3-none-any.whl", hash = "sha256:e77e7972b5ac6a883b3778b0273d9582c5b8ae847ac78fba3d080dd4a4cc69ad"},
{file = "usb_devices-0.5.1.tar.gz", hash = "sha256:8ef45c401490391a3651b3ba748eb213a654ac165be10a0786ab963668c49cc3"},
]
[[package]]
name = "winrt-runtime"
version = "3.2.1"
description = "Python projection of Windows Runtime (WinRT) APIs"
optional = false
python-versions = ">=3.9"
groups = ["main"]
markers = "sys_platform == \"win32\""
files = [
{file = "winrt_runtime-3.2.1-cp310-cp310-win32.whl", hash = "sha256:25a2d1e2b45423742319f7e10fa8ca2e7063f01284b6e85e99d805c4b50bbfb3"},
{file = "winrt_runtime-3.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:dc81d5fb736bf1ddecf743928622253dce4d0aac9a57faad776d7a3834e13257"},
{file = "winrt_runtime-3.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:363f584b1e9fcb601e3e178636d8877e6f0537ac3c96ce4a96f06066f8ff0eae"},
{file = "winrt_runtime-3.2.1-cp311-cp311-win32.whl", hash = "sha256:9e9b64f1ba631cc4b9fe60b8ff16fef3f32c7ce2fcc84735a63129ff8b15c022"},
{file = "winrt_runtime-3.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:c0a9046ae416808420a358c51705af8ae100acd40bc578be57ddfdd51cbb0f9c"},
{file = "winrt_runtime-3.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:e94f3cb40ea2d723c44c82c16d715c03c6b3bd977d135b49535fdd5415fd9130"},
{file = "winrt_runtime-3.2.1-cp312-cp312-win32.whl", hash = "sha256:762b3d972a2f7037f7db3acbaf379dd6d8f6cda505f71f66c6b425d1a1eae2f1"},
{file = "winrt_runtime-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:06510db215d4f0dc45c00fbb1251c6544e91742a0ad928011db33b30677e1576"},
{file = "winrt_runtime-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:14562c29a087ccad38e379e585fef333e5c94166c807bdde67b508a6261aa195"},
{file = "winrt_runtime-3.2.1-cp313-cp313-win32.whl", hash = "sha256:44e2733bc709b76c554aee6c7fe079443b8306b2e661e82eecfebe8b9d71e4d1"},
{file = "winrt_runtime-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:3c1fdcaeedeb2920dc3b9039db64089a6093cad2be56a3e64acc938849245a6d"},
{file = "winrt_runtime-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:28f3dab083412625ff4d2b46e81246932e6bebddf67bea7f05e01712f54e6159"},
{file = "winrt_runtime-3.2.1-cp314-cp314-win32.whl", hash = "sha256:9b6298375468ac2f6815d0c008a059fc16508c8f587e824c7936ed9216480dad"},
{file = "winrt_runtime-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:e36e587ab5fd681ee472cd9a5995743f75107a1a84d749c64f7e490bc86bc814"},
{file = "winrt_runtime-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:35d6241a2ebd5598e4788e69768b8890ee1eee401a819865767a1fbdd3e9a650"},
{file = "winrt_runtime-3.2.1-cp39-cp39-win32.whl", hash = "sha256:07c0cb4a53a4448c2cb7597b62ae8c94343c289eeebd8f83f946eb2c817bde01"},
{file = "winrt_runtime-3.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:1856325ca3354b45e0789cf279be9a882134085d34214946db76110d98391efa"},
{file = "winrt_runtime-3.2.1-cp39-cp39-win_arm64.whl", hash = "sha256:cf237858de1d62e4c9b132c66b52028a7a3e8534e8ab90b0e29a68f24f7be39d"},
{file = "winrt_runtime-3.2.1.tar.gz", hash = "sha256:c8dca19e12b234ae6c3dadf1a4d0761b51e708457492c13beb666556958801ea"},
]
[package.dependencies]
typing_extensions = ">=4.12.2"
[[package]]
name = "winrt-windows-devices-bluetooth"
version = "3.2.1"
description = "Python projection of Windows Runtime (WinRT) APIs"
optional = false
python-versions = ">=3.9"
groups = ["main"]
markers = "sys_platform == \"win32\""
files = [
{file = "winrt_windows_devices_bluetooth-3.2.1-cp310-cp310-win32.whl", hash = "sha256:49489351037094a088a08fbdf0f99c94e3299b574edb211f717c4c727770af78"},
{file = "winrt_windows_devices_bluetooth-3.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:20f6a21029034c18ea6a6b6df399671813b071102a0d6d8355bb78cf4f547cdb"},
{file = "winrt_windows_devices_bluetooth-3.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:69c523814eab795bc1bf913292309cb1025ef0a67d5fc33863a98788995e551d"},
{file = "winrt_windows_devices_bluetooth-3.2.1-cp311-cp311-win32.whl", hash = "sha256:f4082a00b834c1e34b961e0612f3e581356bdb38c5798bd6842f88ec02e5152b"},
{file = "winrt_windows_devices_bluetooth-3.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:44277a3f2cc5ac32ce9b4b2d96c5c5f601d394ac5f02cc71bcd551f738660e2d"},
{file = "winrt_windows_devices_bluetooth-3.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:0803a417403a7d225316b9b0c4fe3f8446579d6a22f2f729a2c21f4befc74a80"},
{file = "winrt_windows_devices_bluetooth-3.2.1-cp312-cp312-win32.whl", hash = "sha256:18c833ec49e7076127463679e85efc59f61785ade0dc185c852586b21be1f31c"},
{file = "winrt_windows_devices_bluetooth-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:9b6702c462b216c91e32388023a74d0f87210cef6fd5d93b7191e9427ce2faca"},
{file = "winrt_windows_devices_bluetooth-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:419fd1078c7749119f6b4bbf6be4e586e03a0ed544c03b83178f1d85f1b3d148"},
{file = "winrt_windows_devices_bluetooth-3.2.1-cp313-cp313-win32.whl", hash = "sha256:12b0a16fb36ce0b42243ca81f22a6b53fbb344ed7ea07a6eeec294604f0505e4"},
{file = "winrt_windows_devices_bluetooth-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:6703dfbe444ee22426738830fb305c96a728ea9ccce905acfdf811d81045fdb3"},
{file = "winrt_windows_devices_bluetooth-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:2cf8a0bfc9103e32dc7237af15f84be06c791f37711984abdca761f6318bbdb2"},
{file = "winrt_windows_devices_bluetooth-3.2.1-cp314-cp314-win32.whl", hash = "sha256:de36ded53ca3ba12fc6dd4deb14b779acc391447726543815df4800348aad63a"},
{file = "winrt_windows_devices_bluetooth-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:3295d932cc93259d5ccb23a41e3a3af4c78ce5d6a6223b2b7638985f604fa34c"},
{file = "winrt_windows_devices_bluetooth-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:1f61c178766a1bbce0669f44790c6161ff4669404c477b4aedaa576348f9e102"},
{file = "winrt_windows_devices_bluetooth-3.2.1-cp39-cp39-win32.whl", hash = "sha256:32fc355bfdc5d6b3b1875df16eaf12f9b9fc0445e01177833c27d9a4fc0d50b6"},
{file = "winrt_windows_devices_bluetooth-3.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:b886ef1fc0ed49163ae6c2422dd5cb8dd4709da7972af26c8627e211872818d0"},
{file = "winrt_windows_devices_bluetooth-3.2.1-cp39-cp39-win_arm64.whl", hash = "sha256:8643afa53f9fb8fe3b05967227f86f0c8e1d7b822289e60a848c6368acc977d2"},
{file = "winrt_windows_devices_bluetooth-3.2.1.tar.gz", hash = "sha256:db496d2d92742006d5a052468fc355bf7bb49e795341d695c374746113d74505"},
]
[package.dependencies]
winrt-runtime = ">=3.2.1.0,<3.3.0.0"
[package.extras]
all = ["winrt-Windows.Devices.Bluetooth.GenericAttributeProfile[all] (>=3.2.1.0,<3.3.0.0)", "winrt-Windows.Devices.Bluetooth.Rfcomm[all] (>=3.2.1.0,<3.3.0.0)", "winrt-Windows.Devices.Enumeration[all] (>=3.2.1.0,<3.3.0.0)", "winrt-Windows.Devices.Radios[all] (>=3.2.1.0,<3.3.0.0)", "winrt-Windows.Foundation.Collections[all] (>=3.2.1.0,<3.3.0.0)", "winrt-Windows.Foundation[all] (>=3.2.1.0,<3.3.0.0)", "winrt-Windows.Networking[all] (>=3.2.1.0,<3.3.0.0)", "winrt-Windows.Storage.Streams[all] (>=3.2.1.0,<3.3.0.0)"]
[[package]]
name = "winrt-windows-devices-bluetooth-advertisement"
version = "3.2.1"
description = "Python projection of Windows Runtime (WinRT) APIs"
optional = false
python-versions = ">=3.9"
groups = ["main"]
markers = "sys_platform == \"win32\""
files = [
{file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp310-cp310-win32.whl", hash = "sha256:a758c5f81a98cc38347fdfb024ce62720969480e8c5b98e402b89d2b09b32866"},
{file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:f982ef72e729ddd60cdb975293866e84bb838798828933012a57ee4bf12b0ea1"},
{file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:e88a72e1e09c7ccc899a9e6d2ab3fc0f43b5dd4509bcc49ec4abf65b55ab015f"},
{file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp311-cp311-win32.whl", hash = "sha256:fe17c2cf63284646622e8b2742b064bf7970bbf53cfab02062136c67fa6b06c9"},
{file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:78e99dd48b4d89b71b7778c5085fdba64e754dd3ebc54fd09c200fe5222c6e09"},
{file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:6d5d2295474deab444fc4311580c725a2ca8a814b0f3344d0779828891d75401"},
{file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp312-cp312-win32.whl", hash = "sha256:901933cc40de5eb7e5f4188897c899dd0b0f577cb2c13eab1a63c7dfe89b08c4"},
{file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:e6c66e7d4f4ca86d2c801d30efd2b9673247b59a2b4c365d9e11650303d68d89"},
{file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:447d19defd8982d39944642eb7ebe89e4e20259ec9734116cf88879fb2c514ff"},
{file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp313-cp313-win32.whl", hash = "sha256:4122348ea525a914e85615647a0b54ae8b2f42f92cdbf89c5a12eea53ef6ed90"},
{file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:b66410c04b8dae634a7e4b615c3b7f8adda9c7d4d6902bcad5b253da1a684943"},
{file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:07af19b1d252ddb9dd3eb2965118bc2b7cabff4dda6e499341b765e5038ca61d"},
{file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp314-cp314-win32.whl", hash = "sha256:2985565c265b3f9eab625361b0e40e88c94b03d89f5171f36146f2e88b3ee214"},
{file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:d102f3fac64fde32332e370969dfbc6f37b405d8cc055d9da30d14d07449a3c2"},
{file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:ffeb5e946cd42c32c6999a62e240d6730c653cdfb7b49c7839afba375e20a62a"},
{file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp39-cp39-win32.whl", hash = "sha256:6c4747d2e5b0e2ef24e9b84a848cf8fc50fb5b268a2086b5ee8680206d1e0197"},
{file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:18d4c5d8b80ee2d29cc13c2fc1353fdb3c0f620c8083701c9b9ecf5e6c503c8d"},
{file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp39-cp39-win_arm64.whl", hash = "sha256:75dd856611d847299078d56aee60e319df52975b931c992cd1d32ad5143fe772"},
{file = "winrt_windows_devices_bluetooth_advertisement-3.2.1.tar.gz", hash = "sha256:0223852a7b7fa5c8dea3c6a93473bd783df4439b1ed938d9871f947933e574cc"},
]
[package.dependencies]
winrt-runtime = ">=3.2.1.0,<3.3.0.0"
[package.extras]
all = ["winrt-Windows.Devices.Bluetooth[all] (>=3.2.1.0,<3.3.0.0)", "winrt-Windows.Foundation.Collections[all] (>=3.2.1.0,<3.3.0.0)", "winrt-Windows.Foundation[all] (>=3.2.1.0,<3.3.0.0)", "winrt-Windows.Storage.Streams[all] (>=3.2.1.0,<3.3.0.0)"]
[[package]]
name = "winrt-windows-devices-bluetooth-genericattributeprofile"
version = "3.2.1"
description = "Python projection of Windows Runtime (WinRT) APIs"
optional = false
python-versions = ">=3.9"
groups = ["main"]
markers = "sys_platform == \"win32\""
files = [
{file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp310-cp310-win32.whl", hash = "sha256:af4914d7b30b49232092cd3b934e3ed6f5d3b1715ba47238541408ee595b7f46"},
{file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:0e557dd52fc80392b8bd7c237e1153a50a164b3983838b4ac674551072efc9ed"},
{file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:64cff62baa6b7aadd6c206e61d149113fdcda17360feb6e9d05bc8bbda4b9fde"},
{file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp311-cp311-win32.whl", hash = "sha256:832cf65d035a11e6dbfef4fd66abdcc46be7e911ec96e2e72e98e12d8d5b9d3c"},
{file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:8179638a6c721b0bbf04ba251ef98d5e02d9a17f0cce377398e42c4fbb441415"},
{file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:70b7edfca3190b89ae38bf60972b11978311b6d933d3142ae45560c955dbf5c7"},
{file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp312-cp312-win32.whl", hash = "sha256:ef894d21e0a805f3e114940254636a8045335fa9de766c7022af5d127dfad557"},
{file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:db05de95cd1b24a51abb69cb936a8b17e9214e015757d0b37e3a5e207ddceb3d"},
{file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:8d4e131cf3d15fc5ad81c1bcde3509ac171298217381abed6bdf687f29871984"},
{file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp313-cp313-win32.whl", hash = "sha256:b1879c8dcf46bd2110b9ad4b0b185f4e2a5f95170d014539203a5fee2b2115f0"},
{file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d8d89f01e9b6931fb48217847caac3227a0aeb38a5b7782af71c2e7b262ec30"},
{file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:4e71207bb89798016b1795bb15daf78afe45529f2939b3b9e78894cfe650b383"},
{file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp314-cp314-win32.whl", hash = "sha256:d5f83739ca370f0baf52b0400aebd6240ab80150081fbfba60fd6e7b2e7b4c5f"},
{file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:13786a5853a933de140d456cd818696e1121c7c296ae7b7af262fc5d2cffb851"},
{file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:5140682da2860f6a55eb6faf9e980724dc457c2e4b4b35a10e1cebd8fc97d892"},
{file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp39-cp39-win32.whl", hash = "sha256:963339a0161f9970b577a6193924be783978d11693da48b41a025f61b3c5562a"},
{file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:d43615c5dfa939dd30fe80dc0649434a13cc7cf0294ad0d7283d5a9f48c6ce86"},
{file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp39-cp39-win_arm64.whl", hash = "sha256:8e70fa970997e2e67a8a4172bc00b0b2a79b5ff5bb2668f79cf10b3fd63d3974"},
{file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1.tar.gz", hash = "sha256:cdf6ddc375e9150d040aca67f5a17c41ceaf13a63f3668f96608bc1d045dde71"},
]
[package.dependencies]
winrt-runtime = ">=3.2.1.0,<3.3.0.0"
[package.extras]
all = ["winrt-Windows.Devices.Bluetooth[all] (>=3.2.1.0,<3.3.0.0)", "winrt-Windows.Devices.Enumeration[all] (>=3.2.1.0,<3.3.0.0)", "winrt-Windows.Foundation.Collections[all] (>=3.2.1.0,<3.3.0.0)", "winrt-Windows.Foundation[all] (>=3.2.1.0,<3.3.0.0)", "winrt-Windows.Storage.Streams[all] (>=3.2.1.0,<3.3.0.0)"]
[[package]]
name = "winrt-windows-devices-enumeration"
version = "3.2.1"
description = "Python projection of Windows Runtime (WinRT) APIs"
optional = false
python-versions = ">=3.9"
groups = ["main"]
markers = "sys_platform == \"win32\""
files = [
{file = "winrt_windows_devices_enumeration-3.2.1-cp310-cp310-win32.whl", hash = "sha256:40dac777d8f45b41449f3ff1ae70f0d457f1ede53f53962a6e2521b651533db5"},
{file = "winrt_windows_devices_enumeration-3.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:a101ec3e0ad0a0783032fdcd5dc48e7cd68ee034cbde4f903a8c7b391532c71a"},
{file = "winrt_windows_devices_enumeration-3.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:3296a3863ac086928ff3f3dc872b2a2fb971dab728817424264f3ca547504e9e"},
{file = "winrt_windows_devices_enumeration-3.2.1-cp311-cp311-win32.whl", hash = "sha256:9f29465a6c6b0456e4330d4ad09eccdd53a17e1e97695c2e57db0d4666cc0011"},
{file = "winrt_windows_devices_enumeration-3.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2a725d04b4cb43aa0e2af035f73a60d16a6c0ff165fcb6b763383e4e33a975fd"},
{file = "winrt_windows_devices_enumeration-3.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:6365ef5978d4add26678827286034acf474b6b133aa4054e76567d12194e6817"},
{file = "winrt_windows_devices_enumeration-3.2.1-cp312-cp312-win32.whl", hash = "sha256:1db22b0292b93b0688d11ad932ad1f3629d4f471310281a2fbfe187530c2c1f3"},
{file = "winrt_windows_devices_enumeration-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:a73bc88d7f510af454f2b392985501c96f39b89fd987140708ccaec1588ceebc"},
{file = "winrt_windows_devices_enumeration-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:2853d687803f0dd76ae1afe3648abc0453e09dff0e7eddbb84b792eddb0473ca"},
{file = "winrt_windows_devices_enumeration-3.2.1-cp313-cp313-win32.whl", hash = "sha256:14a71cdcc84f624c209cbb846ed6bd9767a9a9437b2bf26b48ac9a91599da6e9"},
{file = "winrt_windows_devices_enumeration-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:6ca40d334734829e178ad46375275c4f7b5d6d2d4fc2e8879690452cbfb36015"},
{file = "winrt_windows_devices_enumeration-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:2d14d187f43e4409c7814b7d1693c03a270e77489b710d92fcbbaeca5de260d4"},
{file = "winrt_windows_devices_enumeration-3.2.1-cp314-cp314-win32.whl", hash = "sha256:e087364273ed7c717cd0191fed4be9def6fdf229fe9b536a4b8d0228f7814106"},
{file = "winrt_windows_devices_enumeration-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:0da1ddb8285d97a6775c36265d7157acf1bbcb88bcc9a7ce9a4549906c822472"},
{file = "winrt_windows_devices_enumeration-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:09bf07e74e897e97a49a9275d0a647819254ddb74142806bbbcf4777ed240a22"},
{file = "winrt_windows_devices_enumeration-3.2.1-cp39-cp39-win32.whl", hash = "sha256:986e8d651b769a0e60d2834834bdd3f6959f6a88caa0c9acb917797e6b43a588"},
{file = "winrt_windows_devices_enumeration-3.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:10da7d403ac4afd385fe13bd5808c9a5dd616a8ef31ca5c64cea3f87673661c1"},
{file = "winrt_windows_devices_enumeration-3.2.1-cp39-cp39-win_arm64.whl", hash = "sha256:679e471d21ac22cb50de1bf4dfc4c0c3f5da9f3e3fbc7f08dcacfe9de9d6dd58"},
{file = "winrt_windows_devices_enumeration-3.2.1.tar.gz", hash = "sha256:df316899e39bfc0ffc1f3cb0f5ee54d04e1d167fbbcc1484d2d5121449a935cf"},
]
[package.dependencies]
winrt-runtime = ">=3.2.1.0,<3.3.0.0"
[package.extras]
all = ["winrt-Windows.ApplicationModel.Background[all] (>=3.2.1.0,<3.3.0.0)", "winrt-Windows.Foundation.Collections[all] (>=3.2.1.0,<3.3.0.0)", "winrt-Windows.Foundation[all] (>=3.2.1.0,<3.3.0.0)", "winrt-Windows.Security.Credentials[all] (>=3.2.1.0,<3.3.0.0)", "winrt-Windows.Storage.Streams[all] (>=3.2.1.0,<3.3.0.0)", "winrt-Windows.UI.Popups[all] (>=3.2.1.0,<3.3.0.0)", "winrt-Windows.UI[all] (>=3.2.1.0,<3.3.0.0)"]
[[package]]
name = "winrt-windows-devices-radios"
version = "3.2.1"
description = "Python projection of Windows Runtime (WinRT) APIs"
optional = false
python-versions = ">=3.9"
groups = ["main"]
markers = "sys_platform == \"win32\""
files = [
{file = "winrt_windows_devices_radios-3.2.1-cp310-cp310-win32.whl", hash = "sha256:f97766fd551d06c102155d51b2922f96663dee045e1f8d57177def0a2149cb78"},
{file = "winrt_windows_devices_radios-3.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:104b737fa1279a3b6a88ba3c6236157afc1de03c472657c45e5176ad7a209e23"},
{file = "winrt_windows_devices_radios-3.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:55b02877d2de06ca6f0f6140611a9af9d0c65710e28f1afdeaac1040433b1837"},
{file = "winrt_windows_devices_radios-3.2.1-cp311-cp311-win32.whl", hash = "sha256:7c02790472414b6cda00d24a8cd23bca18e4b7474ddad4f9264f4484b891807e"},
{file = "winrt_windows_devices_radios-3.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:f87745486d313ba1e7562ca97f25ad436ec01ad4b3b9ea349fb6b6f25cb41104"},
{file = "winrt_windows_devices_radios-3.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:6cee6f946ff3a3571850d1ca745edaee7c331d06ca321873e650779654effc4a"},
{file = "winrt_windows_devices_radios-3.2.1-cp312-cp312-win32.whl", hash = "sha256:c3e683ce682338a5a5ed465f735e223ba7a22f16d0bbea2d070962bc7657edbb"},
{file = "winrt_windows_devices_radios-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:a116e552a3f38607b9be558fb2e7de9b4450d1f9080069944d74d80cdda1873e"},
{file = "winrt_windows_devices_radios-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:4c28822f9251c9d547324f596b5c2581f050254ded05e5b786c650a3502744c1"},
{file = "winrt_windows_devices_radios-3.2.1-cp313-cp313-win32.whl", hash = "sha256:ae4a0065927fcd2d10215223f8a46be6fb89bad71cb4edd25dae3d01c137b3a8"},
{file = "winrt_windows_devices_radios-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:bf1a975f46a2aa271ffea1340be0c7e64985050d07433e701343dddc22a72290"},
{file = "winrt_windows_devices_radios-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:10b298ed154c5824cea2de174afce1694ed2aabfb58826de814074027ffef96f"},
{file = "winrt_windows_devices_radios-3.2.1-cp314-cp314-win32.whl", hash = "sha256:21452e1cae50e44cd1d5e78159e1b9986ac3389b66458ad89caa196ce5eca2d6"},
{file = "winrt_windows_devices_radios-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:6a8413e586fe597c6849607885cca7e0549da33ae5699165d11f7911534c6eaf"},
{file = "winrt_windows_devices_radios-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:39129fd9d09103adb003575f59881c1a5a70a43310547850150b46c6f4020312"},
{file = "winrt_windows_devices_radios-3.2.1-cp39-cp39-win32.whl", hash = "sha256:59b868d45ff22afad21b0b0d1466ec43e54543c4e4c6f1efcc2d4adc77053bd5"},
{file = "winrt_windows_devices_radios-3.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:dbfcbb977f60f19c852204987ace0cd6f7a432d735882a45b3074fdbfd3fdb5a"},
{file = "winrt_windows_devices_radios-3.2.1-cp39-cp39-win_arm64.whl", hash = "sha256:659e07e6aa5542587ccfc4d4e2cc6e1ef0869606c867a3e95fc82cc8aeaf1f81"},
{file = "winrt_windows_devices_radios-3.2.1.tar.gz", hash = "sha256:4dc9b9d1501846049eb79428d64ec698d6476c27a357999b78a8331072e18a0b"},
]
[package.dependencies]
winrt-runtime = ">=3.2.1.0,<3.3.0.0"
[package.extras]
all = ["winrt-Windows.Foundation.Collections[all] (>=3.2.1.0,<3.3.0.0)", "winrt-Windows.Foundation[all] (>=3.2.1.0,<3.3.0.0)"]
[[package]]
name = "winrt-windows-foundation"
version = "3.2.1"
description = "Python projection of Windows Runtime (WinRT) APIs"
optional = false
python-versions = ">=3.9"
groups = ["main"]
markers = "sys_platform == \"win32\""
files = [
{file = "winrt_windows_foundation-3.2.1-cp310-cp310-win32.whl", hash = "sha256:677e98165dcbbf7a2367f905bc61090ef2c568b6e465f87cf7276df4734f3b0b"},
{file = "winrt_windows_foundation-3.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:a8f27b4f0fdb73ccc4a3e24bc8010a6607b2bdd722fa799eafce7daa87d19d39"},
{file = "winrt_windows_foundation-3.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:d900c6165fab4ea589811efa2feed27b532e1b6f505f63bf63e2052b8cb6bdc4"},
{file = "winrt_windows_foundation-3.2.1-cp311-cp311-win32.whl", hash = "sha256:d1b5970241ccd61428f7330d099be75f4f52f25e510d82c84dbbdaadd625e437"},
{file = "winrt_windows_foundation-3.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:f3762be2f6e0f2aedf83a0742fd727290b397ffe3463d963d29211e4ebb53a7e"},
{file = "winrt_windows_foundation-3.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:806c77818217b3476e6c617293b3d5b0ff8a9901549dc3417586f6799938d671"},
{file = "winrt_windows_foundation-3.2.1-cp312-cp312-win32.whl", hash = "sha256:867642ccf629611733db482c4288e17b7919f743a5873450efb6d69ae09fdc2b"},
{file = "winrt_windows_foundation-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:45550c5b6c2125cde495c409633e6b1ea5aa1677724e3b95eb8140bfccbe30c9"},
{file = "winrt_windows_foundation-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:94f4661d71cb35ebc52be7af112f2eeabdfa02cb05e0243bf9d6bd2cafaa6f37"},
{file = "winrt_windows_foundation-3.2.1-cp313-cp313-win32.whl", hash = "sha256:3998dc58ed50ecbdbabace1cdef3a12920b725e32a5806d648ad3f4829d5ba46"},
{file = "winrt_windows_foundation-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:6e98617c1e46665c7a56ce3f5d28e252798416d1ebfee3201267a644a4e3c479"},
{file = "winrt_windows_foundation-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:2a8c1204db5c352f6a563130a5a41d25b887aff7897bb677d4ff0b660315aad4"},
{file = "winrt_windows_foundation-3.2.1-cp314-cp314-win32.whl", hash = "sha256:35e973ab3c77c2a943e139302256c040e017fd6ff1a75911c102964603bba1da"},
{file = "winrt_windows_foundation-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:a22a7ebcec0d262e60119cff728f32962a02df60471ded8b2735a655eccc0ef5"},
{file = "winrt_windows_foundation-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:3be7fbae829b98a6a946db4fbaf356b11db1fbcbb5d4f37e7a73ac6b25de8b87"},
{file = "winrt_windows_foundation-3.2.1-cp39-cp39-win32.whl", hash = "sha256:14d5191725301498e4feb744d91f5b46ce317bf3d28370efda407d5c87f4423b"},
{file = "winrt_windows_foundation-3.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:de5e4f61d253a91ba05019dbf4338c43f962bdad935721ced5e7997933994af5"},
{file = "winrt_windows_foundation-3.2.1-cp39-cp39-win_arm64.whl", hash = "sha256:ebbf6e8168398c9ed0c72c8bdde95a406b9fbb9a23e3705d4f0fe28e5a209705"},
{file = "winrt_windows_foundation-3.2.1.tar.gz", hash = "sha256:ad2f1fcaa6c34672df45527d7c533731fdf65b67c4638c2b4aca949f6eec0656"},
]
[package.dependencies]
winrt-runtime = ">=3.2.1.0,<3.3.0.0"
[package.extras]
all = ["winrt-Windows.Foundation.Collections[all] (>=3.2.1.0,<3.3.0.0)"]
[[package]]
name = "winrt-windows-foundation-collections"
version = "3.2.1"
description = "Python projection of Windows Runtime (WinRT) APIs"
optional = false
python-versions = ">=3.9"
groups = ["main"]
markers = "sys_platform == \"win32\""
files = [
{file = "winrt_windows_foundation_collections-3.2.1-cp310-cp310-win32.whl", hash = "sha256:46948484addfc4db981dab35688d4457533ceb54d4954922af41503fddaa8389"},
{file = "winrt_windows_foundation_collections-3.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:899eaa3a93c35bfb1857d649e8dd60c38b978dda7cedd9725fcdbcebba156fd6"},
{file = "winrt_windows_foundation_collections-3.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:c36eb49ad1eba1b32134df768bb47af13cabb9b59f974a3cea37843e2d80e0e6"},
{file = "winrt_windows_foundation_collections-3.2.1-cp311-cp311-win32.whl", hash = "sha256:9b272d9936e7db4840881c5dcf921eb26789ae4ef23fb6ec15e13e19a16254e7"},
{file = "winrt_windows_foundation_collections-3.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:c646a5d442dd6540ade50890081ca118b41f073356e19032d0a5d7d0d38fbc89"},
{file = "winrt_windows_foundation_collections-3.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:2c4630027c93cdd518b0cf4cc726b8fbdbc3388e36d02aa1de190a0fc18ca523"},
{file = "winrt_windows_foundation_collections-3.2.1-cp312-cp312-win32.whl", hash = "sha256:15704eef3125788f846f269cf54a3d89656fa09a1dc8428b70871f717d595ad6"},
{file = "winrt_windows_foundation_collections-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:550dfb8c82fe74d9e0728a2a16a9175cc9e34ca2b8ef758d69b2a398894b698b"},
{file = "winrt_windows_foundation_collections-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:810ad4bd11ab4a74fdbcd3ed33b597ef7c0b03af73fc9d7986c22bcf3bd24f84"},
{file = "winrt_windows_foundation_collections-3.2.1-cp313-cp313-win32.whl", hash = "sha256:4267a711b63476d36d39227883aeb3fb19ac92b88a9fc9973e66fbce1fd4aed9"},
{file = "winrt_windows_foundation_collections-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:5e12a6e75036ee90484c33e204b85fb6785fcc9e7c8066ad65097301f48cdd10"},
{file = "winrt_windows_foundation_collections-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:34b556255562f1b36d07fba933c2bcd9f0db167fa96727a6cbb4717b152ad7a2"},
{file = "winrt_windows_foundation_collections-3.2.1-cp314-cp314-win32.whl", hash = "sha256:33188ed2d63e844c8adfbb82d1d3d461d64aaf78d225ce9c5930421b413c45ab"},
{file = "winrt_windows_foundation_collections-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:d4cfece7e9c0ead2941e55a1da82f20d2b9c8003bb7a8853bb7f999b539f80a4"},
{file = "winrt_windows_foundation_collections-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:3884146fea13727510458f6a14040b7632d5d90127028b9bfd503c6c655d0c01"},
{file = "winrt_windows_foundation_collections-3.2.1-cp39-cp39-win32.whl", hash = "sha256:20610f098b84c87765018cbc71471092197881f3b92e5d06158fad3bfcea2563"},
{file = "winrt_windows_foundation_collections-3.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:e9739775320ac4c0238e1775d94a54e886d621f9995977e65d4feb8b3778c111"},
{file = "winrt_windows_foundation_collections-3.2.1-cp39-cp39-win_arm64.whl", hash = "sha256:e4c6bddb1359d5014ceb45fe2ecd838d4afeb1184f2ea202c2d21037af0d08a3"},
{file = "winrt_windows_foundation_collections-3.2.1.tar.gz", hash = "sha256:0eff1ad0d8d763ad17e9e7bbd0c26a62b27215016393c05b09b046d6503ae6d5"},
]
[package.dependencies]
winrt-runtime = ">=3.2.1.0,<3.3.0.0"
[package.extras]
all = ["winrt-Windows.Foundation[all] (>=3.2.1.0,<3.3.0.0)"]
[[package]]
name = "winrt-windows-storage-streams"
version = "3.2.1"
description = "Python projection of Windows Runtime (WinRT) APIs"
optional = false
python-versions = ">=3.9"
groups = ["main"]
markers = "sys_platform == \"win32\""
files = [
{file = "winrt_windows_storage_streams-3.2.1-cp310-cp310-win32.whl", hash = "sha256:89bb2d667ebed6861af36ed2710757456e12921ee56347946540320dacf6c003"},
{file = "winrt_windows_storage_streams-3.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:48a78e5dc7d3488eb77e449c278bc6d6ac28abcdda7df298462c4112d7635d00"},
{file = "winrt_windows_storage_streams-3.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:da71231d4a554f9f15f1249b4990c6431176f6dfb0e3385c7caa7896f4ca24d6"},
{file = "winrt_windows_storage_streams-3.2.1-cp311-cp311-win32.whl", hash = "sha256:7dace2f9e364422255d0e2f335f741bfe7abb1f4d4f6003622b2450b87c91e69"},
{file = "winrt_windows_storage_streams-3.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:b02fa251a7eef6081eca1a5f64ecf349cfd1ac0ac0c5a5a30be52897d060bed5"},
{file = "winrt_windows_storage_streams-3.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:efdf250140340a75647e8e8ad002782d91308e9fdd1e19470a5b9cc969ae4780"},
{file = "winrt_windows_storage_streams-3.2.1-cp312-cp312-win32.whl", hash = "sha256:77c1f0e004b84347b5bd705e8f0fc63be8cd29a6093be13f1d0869d0d97b7d78"},
{file = "winrt_windows_storage_streams-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:e4508ee135af53e4fc142876abbf4bc7c2a95edfc7d19f52b291a8499cacd6dc"},
{file = "winrt_windows_storage_streams-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:040cb94e6fb26b0d00a00e8b88b06fadf29dfe18cf24ed6cb3e69709c3613307"},
{file = "winrt_windows_storage_streams-3.2.1-cp313-cp313-win32.whl", hash = "sha256:401bb44371720dc43bd1e78662615a2124372e7d5d9d65dfa8f77877bbcb8163"},
{file = "winrt_windows_storage_streams-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:202c5875606398b8bfaa2a290831458bb55f2196a39c1d4e5fa88a03d65ef915"},
{file = "winrt_windows_storage_streams-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:ca3c5ec0aab60895006bf61053a1aca6418bc7f9a27a34791ba3443b789d230d"},
{file = "winrt_windows_storage_streams-3.2.1-cp314-cp314-win32.whl", hash = "sha256:5cd0dbad86fcc860366f6515fce97177b7eaa7069da261057be4813819ba37ee"},
{file = "winrt_windows_storage_streams-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:3c5bf41d725369b9986e6d64bad7079372b95c329897d684f955d7028c7f27a0"},
{file = "winrt_windows_storage_streams-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:293e09825559d0929bbe5de01e1e115f7a6283d8996ab55652e5af365f032987"},
{file = "winrt_windows_storage_streams-3.2.1-cp39-cp39-win32.whl", hash = "sha256:1c630cfdece58fcf82e4ed86c826326123529836d6d4d855ae8e9ceeff67b627"},
{file = "winrt_windows_storage_streams-3.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:d7ff22434a4829d616a04b068a191ac79e008f6c27541bb178c1f6f1fe7a1657"},
{file = "winrt_windows_storage_streams-3.2.1-cp39-cp39-win_arm64.whl", hash = "sha256:fa90244191108f85f6f7afb43a11d365aca4e0722fe8adc62fb4d2c678d0993d"},
{file = "winrt_windows_storage_streams-3.2.1.tar.gz", hash = "sha256:476f522722751eb0b571bc7802d85a82a3cae8b1cce66061e6e758f525e7b80f"},
]
[package.dependencies]
winrt-runtime = ">=3.2.1.0,<3.3.0.0"
[package.extras]
all = ["winrt-Windows.Foundation.Collections[all] (>=3.2.1.0,<3.3.0.0)", "winrt-Windows.Foundation[all] (>=3.2.1.0,<3.3.0.0)", "winrt-Windows.Storage[all] (>=3.2.1.0,<3.3.0.0)", "winrt-Windows.System[all] (>=3.2.1.0,<3.3.0.0)"]
[[package]]
name = "yarl"
version = "1.24.2"
description = "Yet another URL library"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "yarl-1.24.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5249a113065c2b7a958bc699759e359cd61cfc81e3069662208f48f191b7ed12"},
{file = "yarl-1.24.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7f4425fa244fbf530b006d0c5f79ce920114cfff5b4f5f6056e669f8e160fdc0"},
{file = "yarl-1.24.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15c0b5e49d3c44e2a0b93e6a49476c5edad0a7686b92c395765a7ea775572a75"},
{file = "yarl-1.24.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:246d32a53a947c8f0189f5d699cbd4c7036de45d9359e13ba238d1239678c727"},
{file = "yarl-1.24.2-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:64480fb3e4d4ed9ed71c48a91a477384fc342a50ca30071d2f8a88d51d9c9413"},
{file = "yarl-1.24.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:349de4701dc3760b6e876628423a8f147ef4f5599d10aba1e10702075d424ed9"},
{file = "yarl-1.24.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d162677af8d5d3d6ebab8394b021f4d041ac107a4b705873148a77a49dc9e1b2"},
{file = "yarl-1.24.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f5f5c6ec23a9043f2d139cc072f53dd23168d202a334b9b2fda8de4c3e890d90"},
{file = "yarl-1.24.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:60de6742447fbbf697f16f070b8a443f1b5fe6ca3826fbef9fe70ecd5328e643"},
{file = "yarl-1.24.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:acf93187c3710e422368eb768aee98db551ec7c85adc250207a95c16548ab7ac"},
{file = "yarl-1.24.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f4b0352fd41fd34b6651934606268816afd6914d09626f9bcbbf018edb0afb3f"},
{file = "yarl-1.24.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:6b208bb939099b4b297438da4e9b25357f0b1c791888669b963e45b203ea9f36"},
{file = "yarl-1.24.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4b85b8825e631295ff4bc8943f7471d54c533a9360bbe15ebb38e018b555bb8a"},
{file = "yarl-1.24.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e26acf20c26cb4fefc631fdb75aca2a6b8fa8b7b5d7f204fb6a8f1e63c706f53"},
{file = "yarl-1.24.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:819ca24f8eafcfb683c1bd5f44f2f488cea1274eb8944731ffd2e1f10f619342"},
{file = "yarl-1.24.2-cp310-cp310-win_amd64.whl", hash = "sha256:5cb0f995a901c36be096ccbf4c673591c2faabbe96279598ffaec8c030f85bf4"},
{file = "yarl-1.24.2-cp310-cp310-win_arm64.whl", hash = "sha256:f408eace7e22a68b467a0562e0d27d322f91fe3eaaa6f466b962c6cfaea9fa39"},
{file = "yarl-1.24.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:36348bebb147b83818b9d7e673ea4debc75970afc6ffdc7e3975ad05ce5a58c1"},
{file = "yarl-1.24.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a97e42c8a2233f2f279ecadd9e4a037bcb5d813b78435e8eedd4db5a9e9708c"},
{file = "yarl-1.24.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8d027d56f1035e339d1001ac33eceab5b2ec8e42e449787bb75e289fb9a5cd1d"},
{file = "yarl-1.24.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a6377060e7927187a42b7eb202090cbe2b34933a4eeaf90e3bd9e33432e5cae"},
{file = "yarl-1.24.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:17076578bce0049a5ce57d14ad1bded391b68a3b213e9b81b0097b090244999a"},
{file = "yarl-1.24.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:50713f1d4d6be6375bb178bb43d140ee1acb8abe589cd723320b7925a275be1e"},
{file = "yarl-1.24.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:34263e2fa8fb5bb63a0d97706cda38edbad62fddb58c7f12d6acbc092812aa50"},
{file = "yarl-1.24.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49016d82f032b1bd1e10b01078a7d29ae71bf468eeae0ea22df8bab691e60003"},
{file = "yarl-1.24.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3f6d2c216318f8f32038ca3f72501ba08536f0fd18a36e858836b121b2deed9f"},
{file = "yarl-1.24.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:08d3a33218e0c64393e7610284e770409a9c31c429b078bcb24096ed0a783b8f"},
{file = "yarl-1.24.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5d699376c4ca3cba49bbfae3a05b5b70ded572937171ce1e0b8d87118e2ba294"},
{file = "yarl-1.24.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a1cab588b4fa14bea2e55ebea27478adfb05372f47573738e1acc4a36c0b05d2"},
{file = "yarl-1.24.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:ec87ccc31bd21db7ad009d8572c127c1000f268517618a4cc09adba3c2a7f21c"},
{file = "yarl-1.24.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d1dd47a22843b212baa8d74f37796815d43bd046b42a0f41e9da433386c3136b"},
{file = "yarl-1.24.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7b54b9c67c2b06bd7b9a77253d242124b9c95d2c02def5a1144001ee547dd9d5"},
{file = "yarl-1.24.2-cp311-cp311-win_amd64.whl", hash = "sha256:f8fdbcff8b2c7c9284e60c196f693588598ddcee31e11c18e14949ce44519d45"},
{file = "yarl-1.24.2-cp311-cp311-win_arm64.whl", hash = "sha256:b32c37a7a337e90822c45797bf3d79d60875cfcccd3ecc80e9f453d87026c122"},
{file = "yarl-1.24.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b975866c184564c827e0877380f0dae57dcca7e52782128381b72feff6dfceb8"},
{file = "yarl-1.24.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3b075301a2836a0e297b1b658cb6d6135df535d62efefdd60366bd589c2c82f2"},
{file = "yarl-1.24.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ae44649b00947634ab0dab2a374a638f52923a6e67083f2c156cd5cbd1a881d"},
{file = "yarl-1.24.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:507cc19f0b45454e2d6dcd62ff7d062b9f77a2812404e62dbdaec05b50faa035"},
{file = "yarl-1.24.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4c17bad5a530912d2111825d3f05e89bab2dd376aaa8cbc77e449e6db63e576"},
{file = "yarl-1.24.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f5f0cbb112838a4a293985b6ed73948a547dadcc1ba6d2089938e7abdedceef8"},
{file = "yarl-1.24.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ec8356b8a6afcf81fc7aeeef13b1ff7a49dec00f313394bbb9e83830d32ccd7"},
{file = "yarl-1.24.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e7ebcdef69dec6c6451e616f32b622a6d4a2e92b445c992f7c8e5274a6bbc4c"},
{file = "yarl-1.24.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:47a55d6cf6db2f401017a9e96e5288844e5051911fb4e0c8311a3980f5e59a7d"},
{file = "yarl-1.24.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3065657c80a2321225e804048597ad55658a7e76b32d6f5ee4074d04c50401db"},
{file = "yarl-1.24.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:cb84b80d88e19ede158619b80813968713d8d008b0e2497a576e6a0557d50712"},
{file = "yarl-1.24.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:990de4f680b1c217e77ff0d6aa0029f9eb79889c11fb3e9a3942c7eba29c1996"},
{file = "yarl-1.24.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:abb8ec0323b80161e3802da3150ef660b41d0e9be2048b76a363d93eee992c2b"},
{file = "yarl-1.24.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e7977781f83638a4c73e0f88425563d70173e0dfd90ac006a45c65036293ee3c"},
{file = "yarl-1.24.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e30dd55825dc554ec5b66a94953b8eda8745926514c5089dfcacecb9c99b5bd1"},
{file = "yarl-1.24.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dafe10c12ddd4d120d528c4b5599c953bd7b12845347d507b95451195bb6cad"},
{file = "yarl-1.24.2-cp312-cp312-win_arm64.whl", hash = "sha256:044a09d8401fcf8681977faef6d286b8ade1e2d2e9dceda175d1cfa5ca496f30"},
{file = "yarl-1.24.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:491ac9141decf49ee8030199e1ee251cdff0e131f25678817ff6aa5f837a3536"},
{file = "yarl-1.24.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e89418f65eda18f99030386305bd44d7d504e328a7945db1ead514fbe03a0607"},
{file = "yarl-1.24.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cdfcce633b4a4bb8281913c57fcafd4b5933fbc19111a5e3930bbd299d6102f1"},
{file = "yarl-1.24.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:863297ddede92ee49024e9a9b11ecb59f310ca85b60d8537f56bed9bbb5b1986"},
{file = "yarl-1.24.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:374423f70754a2c96942ede36a29d37dc6b0cb8f92f8d009ddf3ed78d3da5488"},
{file = "yarl-1.24.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:33a29b5d00ccbf3219bb3e351d7875739c19481e030779f48cc46a7a71681a9b"},
{file = "yarl-1.24.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a9532c57211730c515341af11fef6e9b61d157487272a096d0c04da445642592"},
{file = "yarl-1.24.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:91e72cf093fd833483a97ee648e0c053c7c629f51ff4a0e7edd84f806b0c5617"},
{file = "yarl-1.24.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b3177bc0a768ef3bacceb4f272632990b7bea352f1b2f1eee9d6d6ff16516f92"},
{file = "yarl-1.24.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e196952aacaf3b232e265ff02980b64d483dc0972bd49bcb061171ff22ac203a"},
{file = "yarl-1.24.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:204e7a61ce99919c0de1bf904ab5d7aa188a129ea8f690a8f76cfb6e2844dc44"},
{file = "yarl-1.24.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b156914620f0b9d78dc1adb3751141daee561cfec796088abb89ed49d220f1a"},
{file = "yarl-1.24.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8372a2b976cf70654b2be6619ab6068acabb35f724c0fda7b277fbf53d66a5cf"},
{file = "yarl-1.24.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f9a1e9b622ca284143aab5d885848686dcd85453bb1ca9abcdb7503e64dc0056"},
{file = "yarl-1.24.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:810e19b685c8c3c5862f6a38160a1f4e4c0916c9390024ec347b6157a45a0992"},
{file = "yarl-1.24.2-cp313-cp313-win_amd64.whl", hash = "sha256:7d37fb7c38f2b6edab0f845c4f85148d4c44204f52bc127021bd2bc9fdbf1656"},
{file = "yarl-1.24.2-cp313-cp313-win_arm64.whl", hash = "sha256:1e831894be7c2954240e49791fa4b50c05a0dc881de2552cfe3ffd8631c7f461"},
{file = "yarl-1.24.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:f9312b3c02d9b3d23840f67952913c9c8721d7f1b7db305289faefa878f364c2"},
{file = "yarl-1.24.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a4f4d6cd615823bfc7fb7e9b5987c3f41666371d870d51058f77e2680fbe9630"},
{file = "yarl-1.24.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0c3063e5c0a8e8e62fae6c2596fa01da1561e4cd1da6fec5789f5cf99a8aefd8"},
{file = "yarl-1.24.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fecd17873a096036c1c87ab3486f1aef7f269ada7f23f7f856f93b1cc7744f14"},
{file = "yarl-1.24.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a46d1ab4ba4d32e6dc80daf8a28ce0bd83d08df52fbc32f3e288663427734535"},
{file = "yarl-1.24.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73e68edf6dfd5f73f9ca127d84e2a6f9213c65bdffb736bda19524c0564fcd14"},
{file = "yarl-1.24.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a296ca617f2d25fbceafb962b88750d627e5984e75732c712154d058ae8d79a3"},
{file = "yarl-1.24.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51b2cf5ec89a8b8470177641ed62a3ba22d74e1e898e06ad53aa77972487208"},
{file = "yarl-1.24.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:310fc687f7b2044ec54e372c8cbe923bb88f5c37bded0d3079e5791c2fc3cf50"},
{file = "yarl-1.24.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:297a2fe352ecf858b30a98f87948746ec16f001d279f84aebdbd3bd965e2f1bd"},
{file = "yarl-1.24.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2a263e76b97bc42bdcd7c5f4953dec1f7cd62a1112fa7f869e57255229390d67"},
{file = "yarl-1.24.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:822519b64cf0b474f1a0aaef1dc621438ea46bb77c94df97a5b4d213a7d8a8b1"},
{file = "yarl-1.24.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b6067060d9dc594899ba83e6db6c48c68d1e494a6dab158156ed86977ca7bcb1"},
{file = "yarl-1.24.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:0063adad533e57171b79db3943b229d40dfafeeee579767f96541f106bac5f1b"},
{file = "yarl-1.24.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ee8e3fb34513e8dc082b586ef4910c98335d43a6fab688cd44d4851bacfce3e8"},
{file = "yarl-1.24.2-cp314-cp314-win_amd64.whl", hash = "sha256:afb00d7fd8e0f285ca29a44cc50df2d622ff2f7a6d933fa641577b5f9d5f3db0"},
{file = "yarl-1.24.2-cp314-cp314-win_arm64.whl", hash = "sha256:68cf6eacd6028ef1142bc4b48376b81566385ca6f9e7dde3b0fa91be08ffcb57"},
{file = "yarl-1.24.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:221ce1dd921ac4f603957f17d7c18c5cc0797fbb52f156941f92e04605d1d67b"},
{file = "yarl-1.24.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5f3224db28173a00d7afacdee07045cc4673dfab2b15492c7ae10deddbece761"},
{file = "yarl-1.24.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c557165320d6244ebe3a02431b2a201a20080e02f41f0cfa0ccc47a183765da8"},
{file = "yarl-1.24.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:904065e6e85b1fa54d0d87438bd58c14c0bad97aad654ad1077fd9d87e8478ed"},
{file = "yarl-1.24.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8cec2a38d70edc10e0e856ceda886af5327a017ccbde8e1de1bd44d300357543"},
{file = "yarl-1.24.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e7484b9361ed222ee1ca5b4337aa4cbdcc4618ce5aff57d9ef1582fd95893fc0"},
{file = "yarl-1.24.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:84f9670b89f34db07f81e53aee83e0b938a3412329d51c8f922488be7fcc4024"},
{file = "yarl-1.24.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:abb2759733d63a28b4956500a5dd57140f26486c92b2caedfb964ab7d9b79dbf"},
{file = "yarl-1.24.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:081c2bf54efe03774d0311172bc04fedf9ca01e644d4cd8c805688e527209bdc"},
{file = "yarl-1.24.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:86746bef442aa479107fe28132e1277237f9c24c2f00b0b0cf22b3ee0904f2bb"},
{file = "yarl-1.24.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:2d07d21d0bc4b17558e8de0b02fbfdf1e347d3bb3699edd00bb92e7c57925420"},
{file = "yarl-1.24.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4fb1ac3fc5fecd8ae7453ea237e4d22b49befa70266dfe1629924245c21a0c7f"},
{file = "yarl-1.24.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4da31a5512ed1729ca8d8aacde3f7faeb8843cde3165d6bcf7f88f74f17bb8aa"},
{file = "yarl-1.24.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:533ded4dceb5f1f3da7906244f4e82cf46cfd40d84c69a1faf5ac506aa65ecbe"},
{file = "yarl-1.24.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7b3a85525f6e7eeabcfdd372862b21ee1915db1b498a04e8bf0e389b607ff0bd"},
{file = "yarl-1.24.2-cp314-cp314t-win_amd64.whl", hash = "sha256:a7624b1ca46ca5d7b864ef0d2f8efe3091454085ee1855b4e992314529972215"},
{file = "yarl-1.24.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e434a45ce2e7a947f951fc5a8944c8cc080b7e59f9c50ae80fd39107cf88126d"},
{file = "yarl-1.24.2-py3-none-any.whl", hash = "sha256:2783d9226db8797636cd6896e4de81feed252d1db72265686c9558d97a4d94b9"},
{file = "yarl-1.24.2.tar.gz", hash = "sha256:9ac374123c6fd7abf64d1fec93962b0bd4ee2c19751755a762a72dd96c0378f8"},
]
[package.dependencies]
idna = ">=2.0"
multidict = ">=4.0"
propcache = ">=0.2.1"
[extras]
docs = ["Sphinx", "myst-parser", "sphinx-rtd-theme"]
[metadata]
lock-version = "2.1"
python-versions = "^3.11"
content-hash = "e33271d915b496e8ab8e050a53ebe3ed608ae938700b943bf99755798e2ede04"
Bluetooth-Devices-xiaomi-ble-741c848/pyproject.toml 0000664 0000000 0000000 00000005176 15222637367 0022311 0 ustar 00root root 0000000 0000000 [tool.poetry]
name = "xiaomi-ble"
version = "1.15.2"
description = "Manage Xiaomi BLE devices"
authors = ["John Carr "]
license = "Apache-2.0"
readme = "README.md"
repository = "https://github.com/bluetooth-devices/xiaomi-ble"
documentation = "https://xiaomi-ble.readthedocs.io"
classifiers = [
"Development Status :: 2 - Pre-Alpha",
"Intended Audience :: Developers",
"Natural Language :: English",
"Operating System :: OS Independent",
"Topic :: Software Development :: Libraries",
]
packages = [
{ include = "xiaomi_ble", from = "src" },
]
[tool.poetry.urls]
"Bug Tracker" = "https://github.com/bluetooth-devices/xiaomi-ble/issues"
"Changelog" = "https://github.com/bluetooth-devices/xiaomi-ble/blob/main/CHANGELOG.md"
[tool.poetry.dependencies]
python = "^3.11"
# Documentation Dependencies
Sphinx = {version = ">=5,<10", optional = true}
sphinx-rtd-theme = {version = ">=1,<4", optional = true}
myst-parser = {version = ">=0.18,<5.2", optional = true}
home-assistant-bluetooth = ">=1.9.2"
sensor-state-data = ">=2.19.0"
bluetooth-sensor-state-data = ">=1.6.1"
bleak-retry-connector = ">=2.13.0"
bluetooth-data-tools = ">=0.3.1"
bleak = ">=0.19.5"
cryptography = ">=46.0.7"
pycryptodomex = ">=3.19.1"
aiohttp = ">=3.10.0"
orjson = ">=3.11.6"
[tool.poetry.extras]
docs = [
"myst-parser",
"sphinx",
"sphinx-rtd-theme",
]
[tool.poetry.group.dev.dependencies]
pytest = "^9.0.3"
pytest-cov = "^7.1.0"
black = "^26.3.1"
isort = ">=6.1,<9.0"
flake8 = "^7.3.0"
mypy = ">=1.19.1,<3.0.0"
pyupgrade = "^3.21.0"
[tool.semantic_release]
branch = "main"
version_toml = ["pyproject.toml:tool.poetry.version"]
version_variables = ["src/xiaomi_ble/__init__.py:__version__"]
build_command = "pip install poetry && poetry build"
[tool.pytest.ini_options]
addopts = "-v -Wdefault --cov=xiaomi_ble --cov-report=term-missing:skip-covered"
pythonpath = ["src"]
[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 = ["xiaomi_ble", "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-Devices-xiaomi-ble-741c848/renovate.json 0000664 0000000 0000000 00000000101 15222637367 0022072 0 ustar 00root root 0000000 0000000 {
"extends": ["github>browniebroke/renovate-configs:python"]
}
Bluetooth-Devices-xiaomi-ble-741c848/setup.py 0000664 0000000 0000000 00000000360 15222637367 0021075 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="xiaomi-ble")
Bluetooth-Devices-xiaomi-ble-741c848/src/ 0000775 0000000 0000000 00000000000 15222637367 0020153 5 ustar 00root root 0000000 0000000 Bluetooth-Devices-xiaomi-ble-741c848/src/xiaomi_ble/ 0000775 0000000 0000000 00000000000 15222637367 0022263 5 ustar 00root root 0000000 0000000 Bluetooth-Devices-xiaomi-ble-741c848/src/xiaomi_ble/__init__.py 0000664 0000000 0000000 00000002542 15222637367 0024377 0 ustar 00root root 0000000 0000000 """Parser for Xiaomi BLE advertisements.
This file is shamlessly copied from the following repository:
https://github.com/Ernst79/bleparser/blob/c42ae922e1abed2720c7fac993777e1bd59c0c93/package/bleparser/Xiaomi.py
MIT License applies.
"""
from __future__ import annotations
from sensor_state_data import (
DeviceClass,
DeviceKey,
SensorDescription,
SensorDeviceInfo,
SensorUpdate,
SensorValue,
Units,
)
from .cloud import (
XiaomiCloudBLEDevice,
XiaomiCloudException,
XiaomiCloudInvalidAuthenticationException,
XiaomiCloudInvalidPasswordException,
XiaomiCloudInvalidUsernameException,
XiaomiCloudTokenFetch,
XiaomiCloudTwoFactorAuthenticationException,
)
from .devices import SLEEPY_DEVICE_MODELS
from .parser import EncryptionScheme, XiaomiBluetoothDeviceData
__version__ = "1.15.2"
__all__ = [
"SLEEPY_DEVICE_MODELS",
"EncryptionScheme",
"XiaomiBluetoothDeviceData",
"SensorDescription",
"SensorDeviceInfo",
"DeviceClass",
"DeviceKey",
"SensorUpdate",
"SensorDeviceInfo",
"SensorValue",
"Units",
"XiaomiCloudBLEDevice",
"XiaomiCloudException",
"XiaomiCloudInvalidAuthenticationException",
"XiaomiCloudInvalidPasswordException",
"XiaomiCloudInvalidUsernameException",
"XiaomiCloudTokenFetch",
"XiaomiCloudTwoFactorAuthenticationException",
]
Bluetooth-Devices-xiaomi-ble-741c848/src/xiaomi_ble/cloud.py 0000664 0000000 0000000 00000040377 15222637367 0023756 0 ustar 00root root 0000000 0000000 import base64
import hashlib
import hmac
import logging
import os
import random
import time
from dataclasses import dataclass
from typing import Any
import aiohttp
import orjson
from Cryptodome.Cipher import ARC4
from yarl import URL
SERVERS = ["cn", "de", "us", "ru", "tw", "sg", "in", "i2"]
_LOGGER = logging.getLogger(__name__)
# Adapted from PiotrMachowski's Xiaomi-cloud-tokens-extractor
# MIT License
#
# Copyright (c) 2020 Piotr Machowski
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
LOGIN_URL = URL("https://account.xiaomi.com/pass/serviceLogin?sid=xiaomiio&_json=true")
LOGIN_URL2 = URL("https://account.xiaomi.com/pass/serviceLoginAuth2")
@dataclass
class XiaomiCloudBLEDevice:
name: str
mac: str
bindkey: str
class XiaomiCloudException(Exception):
"""Raised when an error occurs during Xiaomi Cloud API communication."""
class XiaomiCloudInvalidAuthenticationException(XiaomiCloudException):
"""Raised when an invalid authentication method is provided."""
class XiaomiCloudInvalidUsernameException(XiaomiCloudInvalidAuthenticationException):
"""Raised when an invalid username is provided."""
class XiaomiCloudInvalidPasswordException(XiaomiCloudInvalidAuthenticationException):
"""Raised when an invalid password is provided."""
class XiaomiCloudTwoFactorAuthenticationException(
XiaomiCloudInvalidAuthenticationException
):
"""Raised when two factor authentication is required."""
def __init__(self, message: str, url: str) -> None:
"""Initialize the exception."""
super().__init__(message)
self.url = url
class XiaomiCloudConnector:
"""Encapsulates Xiaomi Cloud API."""
def __init__(
self, username: str, password: str, session: aiohttp.ClientSession
) -> None:
"""Initialize the Xiaomi Cloud API."""
self._username = username
self._password = password
self._agent = self.generate_agent()
self._device_id = self.generate_device_id()
self._session = session
self._sign: str | None = None
self._ssecurity: str | None = None
self.userId: str | None = None
self._cUserId: str | None = None
self._passToken: str | None = None
self._location: str | None = None
self._code: str | None = None
self._serviceToken: str | None = None
async def _login_step_1(self) -> bool:
headers = {
"User-Agent": self._agent,
"Content-Type": "application/x-www-form-urlencoded",
}
cookies = {**self._cookies, "userId": self._username}
response = await self._session.get(LOGIN_URL, headers=headers, cookies=cookies)
valid = response.status == 200 and "_sign" in self.to_json(
await response.text()
)
if valid:
self._sign = self.to_json(await response.text())["_sign"]
return valid
async def _login_step_2(self) -> bool:
url = LOGIN_URL2
headers = {
"User-Agent": self._agent,
"Content-Type": "application/x-www-form-urlencoded",
}
fields = {
"sid": "xiaomiio",
"hash": hashlib.md5(str.encode(self._password)).hexdigest().upper(),
"callback": "https://sts.api.io.mi.com/sts",
"qs": "%3Fsid%3Dxiaomiio%26_json%3Dtrue",
"user": self._username,
"_sign": self._sign,
"_json": "true",
}
response = await self._session.post(
url, headers=headers, params=fields, cookies=self._cookies
)
valid = response is not None and response.status == 200
if valid:
json_resp = self.to_json(await response.text())
valid = "ssecurity" in json_resp and len(str(json_resp["ssecurity"])) > 4
if valid:
self._ssecurity = json_resp["ssecurity"]
self.userId = json_resp["userId"]
self._cUserId = json_resp["cUserId"]
self._passToken = json_resp["passToken"]
self._location = json_resp["location"]
self._code = json_resp["code"]
elif "notificationUrl" in json_resp:
raise XiaomiCloudTwoFactorAuthenticationException(
"Two factor authentication required.", json_resp["notificationUrl"]
)
return valid
async def _login_step_3(self) -> bool:
headers = {
"User-Agent": self._agent,
"Content-Type": "application/x-www-form-urlencoded",
}
response = await self._session.get(
self._location, headers=headers, cookies=self._cookies
)
if response.status == 200:
self._serviceToken = response.cookies.get("serviceToken").value
return response.status == 200
async def login(self) -> bool:
self._cookies = {
"sdkVersion": "accountsdk-18.8.15",
"deviceId": self._device_id,
}
if not await self._login_step_1():
raise XiaomiCloudInvalidUsernameException("Invalid username.")
if not await self._login_step_2():
raise XiaomiCloudInvalidPasswordException("Invalid password.")
if not await self._login_step_3():
raise XiaomiCloudException("Unable to get service token.")
return True
async def get_homes(self, country: str) -> dict[str, Any] | None:
url = self.get_api_url(country) + "/v2/homeroom/gethome"
params = {
"data": '{"fg": true, "fetch_share": true, "fetch_share_dev": true, "limit": 300, "app_ver": 7}' # noqa
}
return await self.execute_api_call_encrypted(url, params)
async def get_devices(
self, country: str, home_id: str, owner_id: str
) -> dict[str, Any] | None:
url = self.get_api_url(country) + "/v2/home/home_device_list"
params = {
"data": '{"home_owner": '
+ str(owner_id)
+ ',"home_id": '
+ str(home_id)
+ ', "limit": 200, "get_split_device": true, "support_smart_home": true}'
}
return await self.execute_api_call_encrypted(url, params)
async def get_dev_cnt(self, country: str) -> dict[str, Any] | None:
url = self.get_api_url(country) + "/v2/user/get_device_cnt"
params = {"data": '{ "fetch_own": true, "fetch_share": true}'}
return await self.execute_api_call_encrypted(url, params)
async def get_beaconkey(self, country: str, did: str) -> dict[str, Any] | None:
url = self.get_api_url(country) + "/v2/device/blt_get_beaconkey"
params = {"data": '{"did":"' + did + '","pdid":1}'}
return await self.execute_api_call_encrypted(url, params)
async def execute_api_call_encrypted(
self, url: str, params: dict[str, Any]
) -> dict[str, Any] | None:
headers = {
"Accept-Encoding": "identity",
"User-Agent": self._agent,
"Content-Type": "application/x-www-form-urlencoded",
"x-xiaomi-protocal-flag-cli": "PROTOCAL-HTTP2",
"MIOT-ENCRYPT-ALGORITHM": "ENCRYPT-RC4",
}
cookies = {
"userId": str(self.userId),
"yetAnotherServiceToken": str(self._serviceToken),
"serviceToken": str(self._serviceToken),
"locale": "en_GB",
"timezone": "GMT+02:00",
"is_daylight": "1",
"dst_offset": "3600000",
"channel": "MI_APP_STORE",
**self._cookies,
}
millis = round(time.time() * 1000)
nonce = self.generate_nonce(millis)
signed_nonce = self.signed_nonce(nonce)
assert self._ssecurity is not None
fields = self.generate_enc_params(
url, "POST", signed_nonce, nonce, params, self._ssecurity
)
response = await self._session.post(
url, headers=headers, cookies=cookies, params=fields
)
if response.status == 200:
decoded = self.decrypt_rc4(
self.signed_nonce(fields["_nonce"]), await response.text()
)
return orjson.loads(decoded)
if response.status > 400 and response.status < 500:
raise XiaomiCloudInvalidAuthenticationException("Authentication failed")
return None
@staticmethod
def get_api_url(country: str) -> str:
return (
"https://"
+ ("" if country == "cn" else (country + "."))
+ "api.io.mi.com/app"
)
def signed_nonce(self, nonce: str) -> str:
assert self._ssecurity is not None
hash_object = hashlib.sha256(
base64.b64decode(self._ssecurity) + base64.b64decode(nonce)
)
return base64.b64encode(hash_object.digest()).decode("utf-8")
@staticmethod
def signed_nonce_sec(nonce: str, ssecurity: str) -> str:
hash_object = hashlib.sha256(
base64.b64decode(ssecurity) + base64.b64decode(nonce)
)
return base64.b64encode(hash_object.digest()).decode("utf-8")
@staticmethod
def generate_nonce(millis: int) -> str:
nonce_bytes = os.urandom(8) + (int(millis / 60000)).to_bytes(4, byteorder="big")
return base64.b64encode(nonce_bytes).decode()
@staticmethod
def generate_agent() -> str:
agent_id = "".join(
map(lambda i: chr(i), [random.randint(65, 69) for _ in range(13)])
)
return f"Android-7.1.1-1.0.0-ONEPLUS A3010-136-{agent_id} APP/xiaomi.smarthome APPV/62830" # noqa
@staticmethod
def generate_device_id() -> str:
return "".join(
map(lambda i: chr(i), [random.randint(97, 122) for _ in range(6)])
)
@staticmethod
def generate_signature(
url: str, signed_nonce: str, nonce: str, params: dict[str, Any]
) -> str:
signature_params: list[str] = [url.split("com")[1], signed_nonce, nonce]
for k, v in params.items():
signature_params.append(f"{k}={v}")
signature_string = "&".join(signature_params)
signature = hmac.new(
base64.b64decode(signed_nonce),
msg=signature_string.encode(),
digestmod=hashlib.sha256,
)
return base64.b64encode(signature.digest()).decode()
@staticmethod
def generate_enc_signature(
url: str, method: str, signed_nonce: str, params: dict[str, Any]
) -> str:
signature_params: list[str] = [
str(method).upper(),
url.split("com")[1].replace("/app/", "/"),
]
for k, v in params.items():
signature_params.append(f"{k}={v}")
signature_params.append(signed_nonce)
signature_string = "&".join(signature_params)
return base64.b64encode(
hashlib.sha1(signature_string.encode("utf-8")).digest()
).decode()
@staticmethod
def generate_enc_params(
url: str,
method: str,
signed_nonce: str,
nonce: str,
params: dict[str, Any],
ssecurity: str,
) -> dict[str, Any]:
params["rc4_hash__"] = XiaomiCloudConnector.generate_enc_signature(
url, method, signed_nonce, params
)
for k, v in params.items():
params[k] = XiaomiCloudConnector.encrypt_rc4(signed_nonce, v)
params.update(
{
"signature": XiaomiCloudConnector.generate_enc_signature(
url, method, signed_nonce, params
),
"ssecurity": ssecurity,
"_nonce": nonce,
}
)
return params
@staticmethod
def to_json(response_text: str) -> dict[str, Any]:
"""Convert a response to a JSON object."""
return orjson.loads(response_text.replace("&&&START&&&", ""))
@staticmethod
def encrypt_rc4(password: str, payload: str) -> str:
"""Encrypt a piece of data."""
r = ARC4.new(base64.b64decode(password))
r.encrypt(bytes(1024))
return base64.b64encode(r.encrypt(payload.encode())).decode()
@staticmethod
def decrypt_rc4(password: str, payload: str) -> str:
"""Decrypt a piece of data."""
r = ARC4.new(base64.b64decode(password))
r.encrypt(bytes(1024))
return r.encrypt(base64.b64decode(payload))
class XiaomiCloudTokenFetch:
def __init__(
self, username: str, password: str, session: aiohttp.ClientSession
) -> None:
"""Initialize the Xiaomi Cloud API."""
self._username = username
self._password = password
self._session = session
async def get_device_info(
self, mac: str, servers: list[str] = SERVERS
) -> XiaomiCloudBLEDevice | None:
"""Get the token for a given MAC address."""
formatted_mac = format_mac_upper(mac)
connector = XiaomiCloudConnector(self._username, self._password, self._session)
await connector.login()
assert connector.userId is not None
homes: dict[str, str] = {}
for server in servers:
home_info = await connector.get_homes(server)
if home_info is not None:
for h in home_info["result"]["homelist"]:
homes[h["id"]] = connector.userId
dev_cnt = await connector.get_dev_cnt(server)
if dev_cnt is not None:
for h in dev_cnt["result"]["share"]["share_family"]:
homes[h["home_id"]] = h["home_owner"]
for home_id, owner_id in homes.items():
devices = await connector.get_devices(server, home_id, owner_id)
if (
devices is None
or not devices["result"]
or not devices["result"]["device_info"]
):
continue
device_info: list[dict[str, Any]] = devices["result"]["device_info"]
for device in device_info:
if (
device["mac"] != formatted_mac
or "did" not in device
or "blt" not in device["did"]
):
continue
key_result = await connector.get_beaconkey(server, device["did"])
if (
key_result
and (result := key_result.get("result"))
and (beacon_key := result.get("beaconkey"))
):
_LOGGER.debug(
"Found beacon key for %s: %s (%s)",
formatted_mac,
beacon_key,
device,
)
return XiaomiCloudBLEDevice(
device["name"], device["mac"], beacon_key
)
return None
def format_mac_upper(mac: str) -> str:
"""Format the mac address string to be upper case."""
to_test = mac
if len(to_test) == 17 and to_test.count(":") == 5:
return to_test.upper()
if len(to_test) == 17 and to_test.count("-") == 5:
to_test = to_test.replace("-", "")
elif len(to_test) == 14 and to_test.count(".") == 2:
to_test = to_test.replace(".", "")
if len(to_test) == 12:
# no : included
return ":".join(
to_test.upper()[i : i + 2] for i in range(0, 12, 2) # noqa: E203
)
# Not sure how formatted, return original
return mac
Bluetooth-Devices-xiaomi-ble-741c848/src/xiaomi_ble/const.py 0000664 0000000 0000000 00000006115 15222637367 0023766 0 ustar 00root root 0000000 0000000 """Constants for Xiaomi BLE advertisements."""
from enum import Enum
from sensor_state_data import BaseDeviceClass
TIMEOUT_1DAY = 86400
SERVICE_MIBEACON = "0000fe95-0000-1000-8000-00805f9b34fb"
SERVICE_HHCCJCY10 = "0000fd50-0000-1000-8000-00805f9b34fb"
SERVICE_SCALE1 = "0000181d-0000-1000-8000-00805f9b34fb"
SERVICE_SCALE2 = "0000181b-0000-1000-8000-00805f9b34fb"
# This characteristic contains the current battery level for a HHCCJCY01
# as well as the firmware version
CHARACTERISTIC_BATTERY = "00001a02-0000-1000-8000-00805f9b34fb"
class EncryptionScheme(Enum):
"""Encryption Schemes for Xiaomi MiBeacon."""
# No encryption is needed to use this device
NONE = "none"
# 12 byte encryption key expected
MIBEACON_LEGACY = "mibeacon_legacy"
# 16 byte encryption key expected
MIBEACON_4_5 = "mibeacon_4_5"
class ExtendedBinarySensorDeviceClass(BaseDeviceClass):
"""Device class for additional binary sensors (compared to sensor-state-data)."""
# On means armed (away), Off means disarmed
ARMED = "armed"
# On means asleep, Off means awake
ASLEEP = "asleep"
# On means door left open, Off means door closed
DEVICE_FORCIBLY_REMOVED = "device_forcibly_removed"
# On means door left open, Off means door closed
DOOR_LEFT_OPEN = "door_left_open"
# On means door stuck, Off means clear
DOOR_STUCK = "door_stuck"
# On means fingerprint Ok, Off means fingerprint Not Ok
FINGERPRINT = "fingerprint"
# On means door someone knocking on the door, Off means no knocking
KNOCK_ON_THE_DOOR = "knock_on_the_door"
# On means door pried, Off means door not pried
PRY_THE_DOOR = "pry_the_door"
# On means measurement stabilized, Off means measuring or stepped off
STABILIZED = "stabilized"
# On means toothbrush On, Off means toothbrush Off
TOOTHBRUSH = "toothbrush"
# On means wearing, Off means not wearing
WEARING = "wearing"
# On means antilock turned On, Off means antilOck turned Off
ANTILOCK = "antilock"
# On means childlock Turned On, Off means childlock turned Off
CHILDLOCK = "childlock"
class ExtendedSensorDeviceClass(BaseDeviceClass):
"""Device class for additional sensors (compared to sensor-state-data)."""
# Charging State
CHARGING_STATE = "charging_state"
# Consumable
CONSUMABLE = "consumable"
# Toothbrush counter
COUNTER = "counter"
# Key id
KEY_ID = "key_id"
# Lock method
LOCK_METHOD = "lock_method"
# Toothbrush score
SCORE = "score"
# Has-Someone-Duration
DURATION_DETECTED = "duration_detected"
# No-One-Duration
DURATION_CLEARED = "duration_cleared"
# Low frequency impedance 50 kHz
IMPEDANCE_LOW = "impedance_low"
# High frequency impedance 250 kHz
IMPEDANCE_HIGH = "impedance_high"
# Heart rate
HEART_RATE = "heart_rate"
# User profile id
PROFILE_ID = "profile_id"
# Pressure Present Duration
PRESSURE_PRESENT_DURATION = "pressure_present_duration"
# Pressure Not Present Duration
PRESSURE_NOT_PRESENT_DURATION = "pressure_not_present_duration"
Bluetooth-Devices-xiaomi-ble-741c848/src/xiaomi_ble/devices.py 0000664 0000000 0000000 00000021651 15222637367 0024264 0 ustar 00root root 0000000 0000000 import dataclasses
@dataclasses.dataclass
class DeviceEntry:
name: str
model: str
manufacturer: str = "Xiaomi"
DEVICE_TYPES: dict[int, DeviceEntry] = {
0x0C3C: DeviceEntry(
name="Alarm Clock",
model="CGC1",
),
0x0576: DeviceEntry(
name="3-in-1 Alarm Clock",
model="CGD1",
),
0x066F: DeviceEntry(
name="Temperature/Humidity Sensor",
model="CGDK2",
),
0x0347: DeviceEntry(
name="Temperature/Humidity Sensor",
model="CGG1",
),
0x0B48: DeviceEntry(
name="Temperature/Humidity Sensor",
model="CGG1-ENCRYPTED",
),
0x03D6: DeviceEntry(
name="Door/Window Sensor",
model="CGH1",
),
0x78D1: DeviceEntry(
name="Door/Window Sensor",
model="MC-COMM",
),
0x0A83: DeviceEntry(
name="Motion/Light Sensor",
model="CGPR1",
),
0x03BC: DeviceEntry(
name="Grow Care Garden",
model="GCLS002",
),
0x0098: DeviceEntry(
name="Plant Sensor",
model="HHCCJCY01",
),
0x015D: DeviceEntry(
name="Smart Flower Pot",
model="HHCCPOT002",
),
0x02DF: DeviceEntry(
name="Formaldehyde Sensor",
model="JQJCY01YM",
),
0x0997: DeviceEntry(
name="Smoke Detector",
model="JTYJGD03MI",
),
0x1568: DeviceEntry(
name="Switch (single button)",
model="K9B-1BTN",
),
0x1569: DeviceEntry(
name="Switch (double button)",
model="K9B-2BTN",
),
0x0DFD: DeviceEntry(
name="Switch (triple button)",
model="K9B-3BTN",
),
0x1C10: DeviceEntry(
name="Switch (single button)",
model="K9BB-1BTN",
),
0x3A61: DeviceEntry(
name="Switch (quadruple button)",
model="KS1",
),
0x3E17: DeviceEntry(
name="Switch (quadruple button)",
model="KS1BP",
),
0x1889: DeviceEntry(
name="Door/Window Sensor",
model="MS1BB(MI)",
),
0x2AEB: DeviceEntry(
name="Motion Sensor",
model="HS1BB(MI)",
),
0x3F0F: DeviceEntry(name="Flood and Rain Sensor", model="RS1BB(MI)"),
0x3F4C: DeviceEntry(
name="Seat Pressure Sensor",
model="PS1BB",
manufacturer="Linptech",
),
0x01AA: DeviceEntry(
name="Temperature/Humidity Sensor",
model="LYWSDCGQ",
),
0x045B: DeviceEntry(
name="Temperature/Humidity Sensor",
model="LYWSD02",
),
0x16E4: DeviceEntry(
name="Temperature/Humidity Sensor",
model="LYWSD02MMC",
),
0x2542: DeviceEntry(
name="Temperature/Humidity Sensor",
model="LYWSD02MMC",
),
0x055B: DeviceEntry(
name="Temperature/Humidity Sensor",
model="LYWSD03MMC",
),
0x2832: DeviceEntry(
name="Temperature/Humidity Sensor",
model="MJWSD05MMC",
),
0x4C47: DeviceEntry(
name="Temperature/Humidity Sensor",
model="MJWSD05MMC",
),
0x55B5: DeviceEntry(
name="Temperature/Humidity Sensor",
model="MJWSD06MMC",
),
0x5BEA: DeviceEntry(
name="Temperature/Humidity Sensor",
model="MJWSD06MMC",
),
0x5DB1: DeviceEntry(
name="Temperature/Humidity Sensor",
model="MBS17",
),
0x78DB: DeviceEntry(
name="Temperature/Humidity Sensor",
model="ESM787",
manufacturer="Yanmi",
),
0x098B: DeviceEntry(
name="Door/Window Sensor",
model="MCCGQ02HL",
),
0x06D3: DeviceEntry(
name="Alarm Clock",
model="MHO-C303",
),
0x0387: DeviceEntry(
name="Temperature/Humidity Sensor",
model="MHO-C401",
),
0x07F6: DeviceEntry(
name="Nightlight",
model="MJYD02YL",
),
0x04E9: DeviceEntry(
name="Door Lock",
model="MJZNMSQ01YD",
),
0x00DB: DeviceEntry(
name="Baby Thermometer",
model="MMC-T201-1",
),
0x0391: DeviceEntry(
name="Body Thermometer",
model="MMC-W505",
),
0x03DD: DeviceEntry(
name="Nightlight",
model="MUE4094RT",
),
0x0489: DeviceEntry(
name="Smart Toothbrush",
model="M1S-T500",
),
0x0806: DeviceEntry(
name="Smart Toothbrush",
model="T700",
),
0x1790: DeviceEntry(
name="Smart Toothbrush",
model="T700",
),
0x0A8D: DeviceEntry(
name="Motion Sensor",
model="RTCGQ02LM",
),
0x3531: DeviceEntry(
name="Motion Sensor",
model="XMPIRO2SXS",
),
0x4C60: DeviceEntry(
name="Motion Sensor",
model="XMPIRO2GSXS",
),
0x4683: DeviceEntry(
name="Occupancy Sensor",
model="XMOSB01XS",
),
0x0863: DeviceEntry(
name="Flood Detector",
model="SJWS01LM",
),
0x045C: DeviceEntry(
name="Smart Kettle",
model="V-SK152",
),
0x040A: DeviceEntry(
name="Mosquito Repellent",
model="WX08ZM",
),
0x04E1: DeviceEntry(
name="Magic Cube",
model="XMMF01JQD",
),
0x1203: DeviceEntry(
name="Thermometer",
model="XMWSDJ04MMC",
),
0x1949: DeviceEntry(
name="Switch (double button)",
model="XMWXKG01YL",
),
0x2387: DeviceEntry(
name="Button",
model="XMWXKG01LM",
),
0x098C: DeviceEntry(
name="Door Lock",
model="XMZNMST02YD",
),
0x0599: DeviceEntry(
name="Door Lock",
model="MJZNMS03LM",
),
0x0784: DeviceEntry(
name="Door Lock",
model="XMZNMS04LM",
),
0x0E39: DeviceEntry(
name="Door Lock",
model="XMZNMS08LM",
),
0x07BF: DeviceEntry(
name="Wireless Switch",
model="YLAI003",
),
0x38BB: DeviceEntry(
name="Wireless Switch",
model="PTX_YK1_QMIMB",
),
0x0153: DeviceEntry(
name="Remote Control",
model="YLYK01YL",
),
0x068E: DeviceEntry(
name="Fan Remote Control",
model="YLYK01YL-FANCL",
),
0x04E6: DeviceEntry(
name="Ventilator Fan Remote Control",
model="YLYK01YL-VENFAN",
),
0x03BF: DeviceEntry(
name="Bathroom Heater Remote",
model="YLYB01YL-BHFRC",
),
0x03B6: DeviceEntry(
name="Dimmer Switch",
model="YLKG07YL/YLKG08YL",
),
0x0083: DeviceEntry(
name="Smart Kettle",
model="YM-K1501",
),
0x0113: DeviceEntry(
name="Smart Kettle",
model="YM-K1501EU",
),
0x069E: DeviceEntry(
name="Door Lock",
model="ZNMS16LM",
),
0x069F: DeviceEntry(
name="Door Lock",
model="ZNMS17LM",
),
0x0380: DeviceEntry(
name="Door Lock",
model="DSL-C08",
),
0x11C2: DeviceEntry(
name="Door Lock",
model="Lockin-SV40",
),
0x0DE7: DeviceEntry(
name="Odor Eliminator",
model="SU001-T",
),
0x30D9: DeviceEntry(
name="Body Composition Scale",
model="MJTZC01YM",
),
0x3BD5: DeviceEntry(
name="Body Composition Scale",
model="MJTZC01YM",
),
0x45C9: DeviceEntry(
name="Smart Scale S200",
model="MJTZC02YM",
),
0x48CF: DeviceEntry(
name="Body Composition Scale",
model="MJTZC01YM",
),
0x4C04: DeviceEntry(
name="Smart Scale S200",
model="MJTZC02YM",
),
0x4DCB: DeviceEntry(
name="Smart Scale S200",
model="MJTZC02YM",
),
0x4B05: DeviceEntry(
name="Body Composition Scale",
model="MJTZC03YM",
),
0x4F59: DeviceEntry(
name="Temperature/Humidity Sensor",
model="CGDK3",
),
0x50FB: DeviceEntry(
name="Human Presence Sensor",
model="ES3",
manufacturer="Linptech",
),
0x520B: DeviceEntry(
name="Temperature/Humidity Sensor with Button",
model="KS2BB",
manufacturer="Linptech",
),
0x5968: DeviceEntry(
name="Switch (double button)",
model="XMWS01XS",
),
0x59FA: DeviceEntry(
name="Smart Band 10",
model="M2457B1",
),
0x59FB: DeviceEntry(
name="Smart Band 10 NFC",
model="M2456B1",
),
0x59FC: DeviceEntry(
name="Smart Band 10 Ceramic Edition",
model="M2456B1",
),
0x6188: DeviceEntry(
name="Smart Band 10 Glimmer Edition",
model="M2456B1",
),
0x6A0F: DeviceEntry(
name="Human Presence Sensor",
model="ES5BB",
manufacturer="Linptech",
),
}
SLEEPY_DEVICE_MODELS = {
"CGH1",
"ES3",
"MC-COMM",
"ES5BB",
"PS1BB",
"JTYJGD03MI",
"MCCGQ02HL",
"MS1BB(MI)",
"RTCGQ02LM",
"MMC-W505",
"RS1BB(MI)",
"XMOSB01XS",
"MJTZC01YM",
"MJTZC03YM",
"HS1BB(MI)",
"XMPIRO2SXS",
"XMPIRO2GSXS",
"M2456B1",
"M2457B1",
}
S400_MODELS = {
"MJTZC01YM",
"MJTZC03YM",
}
Bluetooth-Devices-xiaomi-ble-741c848/src/xiaomi_ble/events.py 0000664 0000000 0000000 00000000701 15222637367 0024137 0 ustar 00root root 0000000 0000000 """Event constants for xiaomi-ble."""
from __future__ import annotations
from sensor_state_data.enum import StrEnum
class EventDeviceKeys(StrEnum):
"""Keys for devices that send events."""
# Button
BUTTON = "button"
# Cube
CUBE = "cube"
# Dimmer
DIMMER = "dimmer"
# Error
ERROR = "error"
# Fingerprint
FINGERPRINT = "fingerprint"
# Motion
MOTION = "motion"
# Lock
LOCK = "lock"
Bluetooth-Devices-xiaomi-ble-741c848/src/xiaomi_ble/locks.py 0000775 0000000 0000000 00000005671 15222637367 0023764 0 ustar 00root root 0000000 0000000 """Constants for Xiaomi Locks."""
from enum import Enum
class BleLockMethod(Enum):
"""Methods for opening and closing locks."""
BLUETOOTH = "bluetooth"
PASSWORD = "password" # nosec bandit B105
BIOMETRICS = "biometrics"
KEY_METHOD = "key_method"
TURNTABLE = "turntable_method"
NFC = "nfc_method"
ONE_TIME_PASSWORD = "one_time_password" # nosec bandit B105
TWO_STEP_VERIFICATION = "two_step_verification"
HOMEKIT = "homekit"
COERCION = "coercion_method"
MANUAL = "manual"
AUTOMATIC = "automatic"
ABNORMAL = "abnormal"
# Definition of lock messages
BLE_LOCK_ERROR = {
0xC0DE0000: "frequent_unlocking_with_incorrect_password",
0xC0DE0001: "frequent_unlocking_with_wrong_fingerprints",
0xC0DE0002: "operation_timeout_password_input_timeout",
0xC0DE0003: "lock_picking",
0xC0DE0004: "reset_button_is_pressed",
0xC0DE0005: "the_wrong_key_is_frequently_unlocked",
0xC0DE0006: "foreign_body_in_the_keyhole",
0xC0DE0007: "the_key_has_not_been_taken_out",
0xC0DE0008: "error_nfc_frequently_unlocks",
0xC0DE0009: "timeout_is_not_locked_as_required",
0xC0DE000A: "failure_to_unlock_frequently_in_multiple_ways",
0xC0DE000B: "unlocking_the_face_frequently_fails",
0xC0DE000C: "failure_to_unlock_the_vein_frequently",
0xC0DE000D: "hijacking_alarm",
0xC0DE000E: "unlock_inside_the_door_after_arming",
0xC0DE000F: "palmprints_frequently_fail_to_unlock",
0xC0DE0010: "the_safe_was_moved",
0xC0DE1000: "the_battery_level_is_less_than_10_percent",
0xC0DE1001: "the_battery_level_is_less_than_5_percent",
0xC0DE1002: "the_fingerprint_sensor_is_abnormal",
0xC0DE1003: "the_accessory_battery_is_low",
0xC0DE1004: "mechanical_failure",
0xC0DE1005: "the_lock_sensor_is_faulty",
}
BLE_LOCK_ACTION: dict[int, tuple[bool, str, str]] = {
0b0000: (True, "lock", "unlock_outside_the_door"),
0b0001: (False, "lock", "locked"),
0b0010: (False, "antilock", "turn_on_antilock"),
0b0011: (True, "antilock", "release_the_antilock"),
0b0100: (True, "lock", "unlock_inside_the_door"),
0b0101: (False, "lock", "lock_inside_the_door"),
0b0110: (False, "childlock", "turn_on_child_lock"),
0b0111: (True, "childlock", "turn_off_child_lock"),
0b1000: (False, "lock", "lock_outside_the_door"),
0b1111: (True, "lock", "abnormal"),
}
BLE_LOCK_METHOD: dict[int, BleLockMethod] = {
0b0000: BleLockMethod.BLUETOOTH,
0b0001: BleLockMethod.PASSWORD,
0b0010: BleLockMethod.BIOMETRICS,
0b0011: BleLockMethod.KEY_METHOD,
0b0100: BleLockMethod.TURNTABLE,
0b0101: BleLockMethod.NFC,
0b0110: BleLockMethod.ONE_TIME_PASSWORD,
0b0111: BleLockMethod.TWO_STEP_VERIFICATION,
0b1001: BleLockMethod.HOMEKIT,
0b1000: BleLockMethod.COERCION,
0b1010: BleLockMethod.MANUAL,
0b1011: BleLockMethod.AUTOMATIC,
0b1111: BleLockMethod.ABNORMAL,
}
Bluetooth-Devices-xiaomi-ble-741c848/src/xiaomi_ble/parser.py 0000664 0000000 0000000 00000260733 15222637367 0024144 0 ustar 00root root 0000000 0000000 """Parser for Xiaomi BLE advertisements.
This file is shamlessly copied from the following repository:
https://github.com/Ernst79/bleparser/blob/c42ae922e1abed2720c7fac993777e1bd59c0c93/package/bleparser/xiaomi.py
MIT License applies.
"""
from __future__ import annotations
import datetime
import logging
import math
import struct
from typing import Any
from bleak import BleakClient
from bleak.backends.device import BLEDevice
from bleak_retry_connector import establish_connection
from bluetooth_data_tools import short_address
from bluetooth_sensor_state_data import BluetoothData
from Cryptodome.Cipher import AES
from cryptography.exceptions import InvalidTag
from cryptography.hazmat.primitives.ciphers.aead import AESCCM
from home_assistant_bluetooth import BluetoothServiceInfo
from sensor_state_data import (
BinarySensorDeviceClass,
SensorLibrary,
SensorUpdate,
Units,
)
from .const import (
CHARACTERISTIC_BATTERY,
SERVICE_HHCCJCY10,
SERVICE_MIBEACON,
SERVICE_SCALE1,
SERVICE_SCALE2,
TIMEOUT_1DAY,
EncryptionScheme,
ExtendedBinarySensorDeviceClass,
ExtendedSensorDeviceClass,
)
from .devices import DEVICE_TYPES, S400_MODELS, SLEEPY_DEVICE_MODELS
from .events import EventDeviceKeys
from .locks import BLE_LOCK_ACTION, BLE_LOCK_ERROR, BLE_LOCK_METHOD
_LOGGER = logging.getLogger(__name__)
def to_mac(addr: bytes) -> str:
"""Return formatted MAC address"""
return ":".join(f"{i:02X}" for i in addr)
def to_unformatted_mac(addr: str) -> str:
"""Return unformatted MAC address"""
return "".join(f"{i:02X}" for i in addr[:])
def parse_event_properties(
event_property: str | None, value: int
) -> dict[str, int | None] | None:
"""Convert event property and data to event properties."""
if event_property:
return {event_property: value}
return None
# Structured objects for data conversions
TH_STRUCT = struct.Struct(" dict[str, Any]:
"""Motion"""
# 0x0003 is only used by MUE4094RT, which does not send motion clear.
# This object is therefore added as event (motion detected).
device.fire_event(
key=EventDeviceKeys.MOTION,
event_type="motion_detected",
event_properties=None,
)
return {}
def obj0006(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Fingerprint"""
if len(xobj) == 5:
key_id_bytes = xobj[0:4]
match_byte = xobj[4]
if key_id_bytes == b"\x00\x00\x00\x00":
key_type = "administrator"
elif key_id_bytes == b"\xff\xff\xff\xff":
key_type = "unknown operator"
elif key_id_bytes == b"\xde\xad\xbe\xef":
key_type = "invalid operator"
else:
key_type = str(int.from_bytes(key_id_bytes, "little"))
if match_byte == 0x00:
result = "match_successful"
elif match_byte == 0x01:
result = "match_failed"
elif match_byte == 0x02:
result = "timeout"
elif match_byte == 0x03:
result = "low_quality_too_light_fuzzy"
elif match_byte == 0x04:
result = "insufficient_area"
elif match_byte == 0x05:
result = "skin_is_too_dry"
elif match_byte == 0x06:
result = "skin_is_too_wet"
else:
result = None
fingerprint = True if match_byte == 0x00 else False
# Update fingerprint binary sensor
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.FINGERPRINT,
native_value=fingerprint,
device_class=ExtendedBinarySensorDeviceClass.FINGERPRINT,
name="Fingerprint",
)
# Update key_id sensor
device.update_sensor(
key=ExtendedSensorDeviceClass.KEY_ID,
name="Key id",
device_class=ExtendedSensorDeviceClass.KEY_ID,
native_value=key_type,
native_unit_of_measurement=None,
)
# Fire Fingerprint action event
if result:
device.fire_event(
key=EventDeviceKeys.FINGERPRINT,
event_type=result,
event_properties=None,
)
return {}
def obj0007(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Door"""
door_byte = xobj[0]
if door_byte == 0x00:
# open the door
device.update_predefined_binary_sensor(BinarySensorDeviceClass.DOOR, True)
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.DOOR_STUCK,
native_value=False, # reset door stuck
device_class=ExtendedBinarySensorDeviceClass.DOOR_STUCK,
name="Door stuck",
)
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.KNOCK_ON_THE_DOOR,
native_value=False, # reset knock on the door
device_class=ExtendedBinarySensorDeviceClass.KNOCK_ON_THE_DOOR,
name="Knock on the door",
)
elif door_byte == 0x01:
# close the door
device.update_predefined_binary_sensor(BinarySensorDeviceClass.DOOR, False)
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.DOOR_LEFT_OPEN,
native_value=False, # reset door left open
device_class=ExtendedBinarySensorDeviceClass.DOOR_LEFT_OPEN,
name="Door left open",
)
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.PRY_THE_DOOR,
native_value=False, # reset pry the door
device_class=ExtendedBinarySensorDeviceClass.PRY_THE_DOOR,
name="Pry the door",
)
elif door_byte == 0x02:
# timeout, not closed
device.update_predefined_binary_sensor(BinarySensorDeviceClass.DOOR, True)
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.DOOR_LEFT_OPEN,
native_value=True,
device_class=ExtendedBinarySensorDeviceClass.DOOR_LEFT_OPEN,
name="Door left open",
)
elif door_byte == 0x03:
# knock on the door
device.update_predefined_binary_sensor(BinarySensorDeviceClass.DOOR, False)
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.KNOCK_ON_THE_DOOR,
native_value=True,
device_class=ExtendedBinarySensorDeviceClass.KNOCK_ON_THE_DOOR,
name="Knock on the door",
)
elif door_byte == 0x04:
# pry the door
device.update_predefined_binary_sensor(BinarySensorDeviceClass.DOOR, True)
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.PRY_THE_DOOR,
native_value=True,
device_class=ExtendedBinarySensorDeviceClass.PRY_THE_DOOR,
name="Pry the door",
)
elif door_byte == 0x05:
# door stuck
device.update_predefined_binary_sensor(BinarySensorDeviceClass.DOOR, False)
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.DOOR_STUCK,
native_value=True,
device_class=ExtendedBinarySensorDeviceClass.DOOR_STUCK,
name="Door stuck",
)
return {}
def obj0008(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""armed away"""
value = xobj[0] ^ 1
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.ARMED,
native_value=bool(value), # Armed away
device_class=ExtendedBinarySensorDeviceClass.ARMED,
name="Armed",
)
# Lift up door handle outside the door sends this event from DSL-C08.
if device_type == "DSL-C08":
device.update_predefined_binary_sensor(
BinarySensorDeviceClass.LOCK, bool(value)
)
# Fire Lock action event
device.fire_event(
key=EventDeviceKeys.LOCK,
event_type="lock_outside_the_door",
event_properties=None,
)
# # Update method sensor
device.update_sensor(
key=ExtendedSensorDeviceClass.LOCK_METHOD,
name="Lock method",
device_class=ExtendedSensorDeviceClass.LOCK_METHOD,
native_value="manual",
native_unit_of_measurement=None,
)
return {}
def obj0010(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Toothbrush"""
if xobj[0] == 0:
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.TOOTHBRUSH,
native_value=True, # Toothbrush On
device_class=ExtendedBinarySensorDeviceClass.TOOTHBRUSH,
name="Toothbrush",
)
if len(xobj) > 1:
device.update_sensor(
key=ExtendedSensorDeviceClass.COUNTER,
name="Counter",
native_unit_of_measurement=None,
device_class=ExtendedSensorDeviceClass.COUNTER,
native_value=xobj[1],
)
else:
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.TOOTHBRUSH,
native_value=False, # Toothbrush Off
device_class=ExtendedBinarySensorDeviceClass.TOOTHBRUSH,
name="Toothbrush",
)
if len(xobj) > 1:
device.update_sensor(
key=ExtendedSensorDeviceClass.SCORE,
name="Score",
native_unit_of_measurement=None,
device_class=ExtendedSensorDeviceClass.SCORE,
native_value=xobj[1],
)
return {}
def obj000a(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Body Temperature"""
if len(xobj) == 2:
temp = T_STRUCT(xobj)[0]
if temp:
device.update_predefined_sensor(
SensorLibrary.TEMPERATURE__CELSIUS, temp / 100
)
return {}
def obj000b(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Lock"""
if len(xobj) == 9:
lock_action_int = xobj[0] & 0x0F
lock_method_int = xobj[0] >> 4
key_id = int.from_bytes(xobj[1:5], "little")
short_key_id = key_id & 0xFFFF
# Lock action (event) and lock method (sensor)
if (
lock_action_int not in BLE_LOCK_ACTION
or lock_method_int not in BLE_LOCK_METHOD
):
return {}
lock_action = BLE_LOCK_ACTION[lock_action_int][2]
lock_method = BLE_LOCK_METHOD[lock_method_int]
# Some specific key_ids represent an error
error = BLE_LOCK_ERROR.get(key_id)
if not error:
if key_id == 0x00000000:
key_type = "administrator"
elif key_id == 0xFFFFFFFF:
key_type = "unknown operator"
elif key_id == 0xDEADBEEF:
key_type = "invalid operator"
elif key_id <= 0x7FFFFFF:
# Bluetooth (up to 2147483647)
key_type = f"Bluetooth key {key_id}"
else:
# All other key methods have only key ids up to 65536
if key_id <= 0x8001FFFF:
key_type = f"Fingerprint key id {short_key_id}"
elif key_id <= 0x8002FFFF:
key_type = f"Password key id {short_key_id}"
elif key_id <= 0x8003FFFF:
key_type = f"Keys key id {short_key_id}"
elif key_id <= 0x8004FFFF:
key_type = f"NFC key id {short_key_id}"
elif key_id <= 0x8005FFFF:
key_type = f"Two-step verification key id {short_key_id}"
elif key_id <= 0x8006FFFF:
key_type = f"Human face key id {short_key_id}"
elif key_id <= 0x8007FFFF:
key_type = f"Finger veins key id {short_key_id}"
elif key_id <= 0x8008FFFF:
key_type = f"Palm print key id {short_key_id}"
else:
key_type = f"key id {short_key_id}"
# Lock type and state
# Lock type can be `lock` or for ZNMS17LM `lock`, `childlock` or `antilock`
if device_type == "ZNMS17LM":
# Lock type can be `lock`, `childlock` or `antilock`
lock_type = BLE_LOCK_ACTION[lock_action_int][1]
else:
# Lock type can only be `lock` for other locks
lock_type = "lock"
lock_state = BLE_LOCK_ACTION[lock_action_int][0]
# Update lock state
if lock_type == "lock":
device.update_predefined_binary_sensor(
BinarySensorDeviceClass.LOCK, lock_state
)
elif lock_type == "childlock":
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.CHILDLOCK,
native_value=lock_state,
device_class=ExtendedBinarySensorDeviceClass.CHILDLOCK,
name="Childlock",
)
elif lock_type == "antilock":
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.ANTILOCK,
native_value=lock_state,
device_class=ExtendedBinarySensorDeviceClass.ANTILOCK,
name="Antilock",
)
else:
return {}
# Update key_id sensor
device.update_sensor(
key=ExtendedSensorDeviceClass.KEY_ID,
name="Key id",
device_class=ExtendedSensorDeviceClass.KEY_ID,
native_value=key_type,
native_unit_of_measurement=None,
)
# Fire Lock action event: see BLE_LOCK_ACTTION
device.fire_event(
key=EventDeviceKeys.LOCK,
event_type=lock_action,
event_properties=None,
)
# # Update method sensor: see BLE_LOCK_METHOD
device.update_sensor(
key=ExtendedSensorDeviceClass.LOCK_METHOD,
name="Lock method",
device_class=ExtendedSensorDeviceClass.LOCK_METHOD,
native_value=lock_method.value,
native_unit_of_measurement=None,
)
if error:
# Fire event with the error: see BLE_LOCK_ERROR
device.fire_event(
key=EventDeviceKeys.ERROR,
event_type=error,
event_properties=None,
)
return {}
def obj000f(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Moving with light"""
if len(xobj) == 3:
illum = LIGHT_STRUCT(xobj + b"\x00")[0]
device.update_predefined_binary_sensor(BinarySensorDeviceClass.MOTION, True)
if device_type in ["MJYD02YL", "RTCGQ02LM"]:
# MJYD02YL: 1 - moving no light, 100 - moving with light
# RTCGQ02LM: 0 - moving no light, 256 - moving with light
device.update_predefined_binary_sensor(
BinarySensorDeviceClass.LIGHT, bool(illum >= 100)
)
elif device_type == "CGPR1":
# CGPR1: moving, value is illumination in lux
device.update_predefined_sensor(SensorLibrary.LIGHT__LIGHT_LUX, illum)
return {}
def obj1001(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""button"""
if len(xobj) != 3:
return {}
button_type, value, press_type = BUTTON_STRUCT(xobj)
# button_type represents the pressed button or rubiks cube rotation direction
remote_command = None
fan_remote_command = None
ven_fan_remote_command = None
bathroom_remote_command = None
cube_rotation = None
one_btn_switch = False
two_btn_switch_left = False
two_btn_switch_right = False
three_btn_switch_left = False
three_btn_switch_middle = False
three_btn_switch_right = False
if button_type == 0:
remote_command = "on"
fan_remote_command = "fan"
ven_fan_remote_command = "swing"
bathroom_remote_command = "stop"
one_btn_switch = True
two_btn_switch_left = True
three_btn_switch_left = True
cube_rotation = "rotate_right"
elif button_type == 1:
remote_command = "off"
fan_remote_command = "light"
ven_fan_remote_command = "power"
bathroom_remote_command = "air_exchange"
two_btn_switch_right = True
three_btn_switch_middle = True
cube_rotation = "rotate_left"
elif button_type == 2:
remote_command = "brightness"
fan_remote_command = "wind_speed"
ven_fan_remote_command = "timer_60_minutes"
bathroom_remote_command = "fan"
two_btn_switch_left = True
two_btn_switch_right = True
three_btn_switch_right = True
elif button_type == 3:
remote_command = "plus"
fan_remote_command = "color_temperature"
ven_fan_remote_command = "increase_wind_speed"
bathroom_remote_command = "increase_speed"
three_btn_switch_left = True
three_btn_switch_middle = True
elif button_type == 4:
remote_command = "M"
fan_remote_command = "wind_mode"
ven_fan_remote_command = "timer_30_minutes"
bathroom_remote_command = "decrease_speed"
three_btn_switch_middle = True
three_btn_switch_right = True
elif button_type == 5:
remote_command = "min"
fan_remote_command = "brightness"
ven_fan_remote_command = "decrease_wind_speed"
bathroom_remote_command = "dry"
three_btn_switch_left = True
three_btn_switch_right = True
elif button_type == 6:
bathroom_remote_command = "light"
three_btn_switch_left = True
three_btn_switch_middle = True
three_btn_switch_right = True
elif button_type == 7:
bathroom_remote_command = "swing"
elif button_type == 8:
bathroom_remote_command = "heat"
# press_type represents the type of press or rotate
# for dimmers, buton_type is used to represent the type of press
# for dimmers, value or button_type is used to represent the direction and number
# of steps, number of presses or duration of long press
button_press_type = "no_press"
btn_switch_press_type = None
dimmer_value: int = 0
if press_type == 0:
button_press_type = "press"
btn_switch_press_type = "press"
elif press_type == 1:
button_press_type = "double_press"
btn_switch_press_type = "long_press"
elif press_type == 2:
button_press_type = "long_press"
btn_switch_press_type = "double_press"
elif press_type == 3:
if button_type == 0:
button_press_type = "press"
dimmer_value = value
if button_type == 1:
button_press_type = "long_press"
dimmer_value = value
elif press_type == 4:
if button_type == 0:
if value <= 127:
button_press_type = "rotate_right"
dimmer_value = value
else:
button_press_type = "rotate_left"
dimmer_value = 256 - value
elif button_type <= 127:
button_press_type = "rotate_right_pressed"
dimmer_value = button_type
else:
button_press_type = "rotate_left_pressed"
dimmer_value = 256 - button_type
elif press_type == 5:
button_press_type = "press"
elif press_type == 6:
button_press_type = "long_press"
# return device specific output
if device_type in ["RTCGQ02LM", "YLAI003", "JTYJGD03MI", "SJWS01LM"]:
# RTCGQ02LM, JTYJGD03MI, SJWS01LM: press
# YLAI003: press, double_press or long_press
device.fire_event(
key=EventDeviceKeys.BUTTON,
event_type=button_press_type,
event_properties=None,
)
elif device_type == "XMMF01JQD":
# cube_rotation: rotate_left or rotate_right
device.fire_event(
key=EventDeviceKeys.CUBE,
event_type=cube_rotation,
event_properties=None,
)
elif device_type == "YLYK01YL":
# Buttons: on, off, brightness, plus, min, M
# Press types: press and long_press
if remote_command == "on":
device.update_predefined_binary_sensor(BinarySensorDeviceClass.POWER, True)
elif remote_command == "off":
device.update_predefined_binary_sensor(BinarySensorDeviceClass.POWER, False)
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_{remote_command}",
event_type=button_press_type,
event_properties=None,
)
elif device_type == "YLYK01YL-FANRC":
# Buttons: fan, light, wind_speed, wind_mode, brightness, color_temperature
# Press types: press and long_press
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_{fan_remote_command}",
event_type=button_press_type,
event_properties=None,
)
elif device_type == "YLYK01YL-VENFAN":
# Buttons: swing, power, timer_30_minutes, timer_60_minutes,
# increase_wind_speed, decrease_wind_speed
# Press types: press and long_press
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_{ven_fan_remote_command}",
event_type=button_press_type,
event_properties=None,
)
elif device_type == "YLYB01YL-BHFRC":
# Buttons: heat, air_exchange, dry, fan, swing, decrease_speed, increase_speed,
# stop or light
# Press types: press and long_press
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_{bathroom_remote_command}",
event_type=button_press_type,
event_properties=None,
)
elif device_type == "YLKG07YL/YLKG08YL":
# Dimmer reports: press, long_press, rotate_left, rotate_right,
# rotate_left_pressed or rotate_right_pressed
if button_press_type == "press":
# it also reports how many times you pressed the dimmer.
event_property = "number_of_presses"
elif button_press_type == "long_press":
# it also reports the duration (in seconds) you pressed the dimmer
event_property = "duration"
elif button_press_type in [
"rotate_right",
"rotate_left",
"rotate_right_pressed",
"rotate_left_pressed",
]:
# it reports how far you rotate, measured in number of `steps`.
event_property = "steps"
else:
event_property = None
event_properties = parse_event_properties(
event_property=event_property, value=dimmer_value
)
device.fire_event(
key=EventDeviceKeys.DIMMER,
event_type=button_press_type,
event_properties=event_properties,
)
elif device_type == "K9B-1BTN":
# Press types: press, double_press, long_press
if one_btn_switch:
device.fire_event(
key=EventDeviceKeys.BUTTON,
event_type=btn_switch_press_type,
event_properties=None,
)
elif device_type == "K9B-2BTN":
# Buttons: left and/or right
# Press types: press, double_press, long_press
# device can send button press of multiple buttons in one message
if two_btn_switch_left:
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_left",
event_type=btn_switch_press_type,
event_properties=None,
)
if two_btn_switch_right:
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_right",
event_type=btn_switch_press_type,
event_properties=None,
)
elif device_type == "K9B-3BTN":
# Buttons: left, middle and/or right
# result can be press, double_press or long_press
# device can send button press of multiple buttons in one message
if three_btn_switch_left:
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_left",
event_type=btn_switch_press_type,
event_properties=None,
)
if three_btn_switch_middle:
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_middle",
event_type=btn_switch_press_type,
event_properties=None,
)
if three_btn_switch_right:
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_right",
event_type=btn_switch_press_type,
event_properties=None,
)
return {}
def obj1004(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Temperature"""
if len(xobj) == 2:
temp = T_STRUCT(xobj)[0]
device.update_predefined_sensor(SensorLibrary.TEMPERATURE__CELSIUS, temp / 10)
return {}
def obj1005(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Power on/off and Temperature"""
device.update_predefined_binary_sensor(BinarySensorDeviceClass.POWER, xobj[0])
device.update_predefined_sensor(SensorLibrary.TEMPERATURE__CELSIUS, xobj[1])
return {}
def obj1006(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Humidity"""
if len(xobj) == 2:
humi = H_STRUCT(xobj)[0]
if device_type in ["LYWSD03MMC", "MHO-C401"]:
# To handle jagged stair stepping readings from these sensors.
# https://github.com/custom-components/ble_monitor/blob/ef2e3944b9c1a635208390b8563710d0eec2a945/custom_components/ble_monitor/sensor.py#L752
# https://github.com/esphome/esphome/blob/c39f6d0738d97ecc11238220b493731ec70c701c/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp#L44C14-L44C99
# https://github.com/custom-components/ble_monitor/issues/7#issuecomment-595948254
device.update_predefined_sensor(
SensorLibrary.HUMIDITY__PERCENTAGE, int(humi / 10)
)
else:
device.update_predefined_sensor(
SensorLibrary.HUMIDITY__PERCENTAGE, humi / 10
)
return {}
def obj1007(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Illuminance"""
if len(xobj) == 3:
illum = ILL_STRUCT(xobj + b"\x00")[0]
if device_type in ["MJYD02YL", "MCCGQ02HL"]:
# 100 means light, else dark (0 or 1)
# MCCGQ02HL might use obj1018 for light sensor, just added here to be sure.
device.update_predefined_binary_sensor(
BinarySensorDeviceClass.LIGHT, illum == 100
)
elif device_type in ["HHCCJCY01", "GCLS002"]:
# illumination in lux
device.update_predefined_sensor(SensorLibrary.LIGHT__LIGHT_LUX, illum)
return {}
def obj1008(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Moisture"""
device.update_predefined_sensor(SensorLibrary.MOISTURE__PERCENTAGE, xobj[0])
return {}
def obj1009(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Conductivity"""
if len(xobj) == 2:
cond = CND_STRUCT(xobj)[0]
device.update_predefined_sensor(SensorLibrary.CONDUCTIVITY__CONDUCTIVITY, cond)
return {}
def obj1010(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Formaldehyde"""
if len(xobj) == 2:
fmdh = FMDH_STRUCT(xobj)[0]
device.update_predefined_sensor(
SensorLibrary.FORMALDEHYDE__CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
fmdh / 100,
)
return {}
def obj1012(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Power on/off"""
device.update_predefined_binary_sensor(BinarySensorDeviceClass.POWER, xobj[0])
return {}
def obj1013(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Consumable (in percent)"""
device.update_sensor(
key=ExtendedSensorDeviceClass.CONSUMABLE,
name="Consumable",
native_unit_of_measurement=Units.PERCENTAGE,
device_class=ExtendedSensorDeviceClass.CONSUMABLE,
native_value=xobj[0],
)
return {}
def obj1014(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Moisture"""
device.update_predefined_binary_sensor(
BinarySensorDeviceClass.MOISTURE, xobj[0] > 0
)
return {}
def obj1015(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Smoke"""
device.update_predefined_binary_sensor(BinarySensorDeviceClass.SMOKE, xobj[0] > 0)
return {}
def obj1017(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Time in seconds without motion"""
if len(xobj) == 4:
no_motion_time = M_STRUCT(xobj)[0]
# seconds since last motion detected message
# 0x1017 is sent 3 seconds after 0x000f, 5 seconds arter 0x1007
# and at 60, 120, 300, 600, 1200 and 1800 seconds after last motion.
# Anything <= 30 seconds is regarded motion detected in the MiHome app.
if no_motion_time <= 30:
device.update_predefined_binary_sensor(BinarySensorDeviceClass.MOTION, True)
else:
device.update_predefined_binary_sensor(
BinarySensorDeviceClass.MOTION, False
)
return {}
def obj1018(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Light intensity"""
device.update_predefined_binary_sensor(BinarySensorDeviceClass.LIGHT, bool(xobj[0]))
return {}
def obj1019(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Door/Window sensor"""
open_obj = xobj[0]
if open_obj == 0:
# opened
device.update_predefined_binary_sensor(BinarySensorDeviceClass.OPENING, True)
elif open_obj == 1:
# closed
device.update_predefined_binary_sensor(BinarySensorDeviceClass.OPENING, False)
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.DOOR_LEFT_OPEN,
native_value=False, # reset door left open
device_class=ExtendedBinarySensorDeviceClass.DOOR_LEFT_OPEN,
name="Door left open",
)
elif open_obj == 2:
# closing timeout
device.update_predefined_binary_sensor(BinarySensorDeviceClass.OPENING, True)
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.DOOR_LEFT_OPEN,
native_value=True,
device_class=ExtendedBinarySensorDeviceClass.DOOR_LEFT_OPEN,
name="Door left open",
)
elif open_obj == 3:
# device reset (not implemented)
return {}
else:
return {}
return {}
def obj100a(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Battery"""
batt = xobj[0]
if device_type in ["LYWSDCGQ", "CGDK3"]:
# Those devices have a single AAA 1.5V battery
volt = 0.8 + (1.6 - 0.8) * (batt / 100)
else:
# The rest has CR2032 3V battery
volt = 2.2 + (3.1 - 2.2) * (batt / 100)
device.update_predefined_sensor(SensorLibrary.BATTERY__PERCENTAGE, batt)
device.update_predefined_sensor(
SensorLibrary.VOLTAGE__ELECTRIC_POTENTIAL_VOLT, volt
)
return {}
def obj100d(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Temperature and humidity"""
if len(xobj) == 4:
temp, humi = TH_STRUCT(xobj)
device.update_predefined_sensor(SensorLibrary.TEMPERATURE__CELSIUS, temp / 10)
device.update_predefined_sensor(SensorLibrary.HUMIDITY__PERCENTAGE, humi / 10)
return {}
def obj100e(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Lock common attribute"""
# https://iot.mi.com/new/doc/accesses/direct-access/embedded-development/ble/object-definition#%E9%94%81%E5%B1%9E%E6%80%A7
if len(xobj) == 1:
# Unlock by type on some devices
if device_type == "DSL-C08":
lock_attribute = int.from_bytes(xobj, "little")
device.update_predefined_binary_sensor(
BinarySensorDeviceClass.LOCK, bool(lock_attribute & 0x01 ^ 1)
)
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.CHILDLOCK,
native_value=bool(lock_attribute >> 3 ^ 1),
device_class=ExtendedBinarySensorDeviceClass.CHILDLOCK,
name="Childlock",
)
return {}
def obj101b(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Timeout no movement"""
# https://iot.mi.com/new/doc/accesses/direct-access/embedded-development/ble/object-definition#%E9%80%9A%E7%94%A8%E5%B1%9E%E6%80%A7
device.update_predefined_binary_sensor(BinarySensorDeviceClass.MOTION, False)
return {}
def obj2000(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Body temperature"""
if len(xobj) == 5:
temp1, temp2, bat = TTB_STRUCT(xobj)
# Body temperature is calculated from the two measured temperatures.
# Formula is based on approximation based on values in the app in
# the range 36.5 - 37.8.
body_temp = (
3.71934 * pow(10, -11) * math.exp(0.69314 * temp1 / 100)
- (1.02801 * pow(10, -8) * math.exp(0.53871 * temp2 / 100))
+ 36.413
)
device.update_predefined_sensor(SensorLibrary.TEMPERATURE__CELSIUS, body_temp)
device.update_predefined_sensor(SensorLibrary.BATTERY__PERCENTAGE, bat)
return {}
def obj3003(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Brushing"""
result = {}
start_obj = xobj[0]
if start_obj == 0:
# Start of brushing
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.TOOTHBRUSH,
native_value=True, # Toothbrush On
device_class=ExtendedBinarySensorDeviceClass.TOOTHBRUSH,
name="Toothbrush",
)
# Start time has not been implemented
start_time = struct.unpack(" dict[str, Any]:
"""Temperature"""
temp = FLOAT_STRUCT(xobj)[0]
device.update_predefined_sensor(SensorLibrary.TEMPERATURE__CELSIUS, round(temp, 1))
return {}
def obj4802(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Humidity"""
device.update_predefined_sensor(SensorLibrary.HUMIDITY__PERCENTAGE, xobj[0])
return {}
def obj4803(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Battery"""
device.update_predefined_sensor(SensorLibrary.BATTERY__PERCENTAGE, xobj[0])
return {}
def obj4804(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Opening (state)"""
opening_state = xobj[0]
# State of the door/window, used in combination with obj4a12
if opening_state == 1:
device.update_predefined_binary_sensor(BinarySensorDeviceClass.OPENING, True)
elif opening_state == 2:
device.update_predefined_binary_sensor(BinarySensorDeviceClass.OPENING, False)
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.DOOR_LEFT_OPEN,
native_value=False, # reset door left open
device_class=ExtendedBinarySensorDeviceClass.DOOR_LEFT_OPEN,
name="Door left open",
)
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.DEVICE_FORCIBLY_REMOVED,
native_value=False, # reset device forcibly removed
device_class=ExtendedBinarySensorDeviceClass.DEVICE_FORCIBLY_REMOVED,
name="Device forcibly removed",
)
return {}
def obj4805(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Illuminance in lux"""
illum = FLOAT_STRUCT(xobj)[0]
device.update_predefined_sensor(SensorLibrary.LIGHT__LIGHT_LUX, illum)
return {}
def obj4806(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Moisture"""
device.update_predefined_binary_sensor(
BinarySensorDeviceClass.MOISTURE, xobj[0] > 0
)
return {}
def obj4808(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Humidity"""
humi = FLOAT_STRUCT(xobj)[0]
device.update_predefined_sensor(SensorLibrary.HUMIDITY__PERCENTAGE, round(humi, 1))
return {}
def obj480c(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Door/Window state for zhiee.magnet.mc01 and similar devices."""
if len(xobj) != 1:
return {}
state = xobj[0]
if state == 0x00:
device.update_predefined_binary_sensor(BinarySensorDeviceClass.OPENING, True)
elif state == 0x01:
device.update_predefined_binary_sensor(BinarySensorDeviceClass.OPENING, False)
return {}
def obj4818(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Time in seconds of no motion"""
if len(xobj) == 2:
(no_motion_time,) = struct.unpack(" dict[str, Any]:
"""Pressure Present State: uint8: 0 - No pressure, 1 - Pressure detected"""
if len(xobj) != 1:
return {}
device.update_predefined_binary_sensor(
BinarySensorDeviceClass.OCCUPANCY, xobj[0] > 0
)
return {}
def obj483d(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Pressure Present Duration in seconds"""
if len(xobj) == 4:
(duration,) = struct.unpack(" dict[str, Any]:
"""Pressure Not Present Duration in seconds"""
if len(xobj) == 4:
(duration,) = struct.unpack(" dict[str, Any]:
"""From miot-spec: occupancy-status: uint8: 0 - No One, 1 - Has One"""
"""Translate to: occupancy: bool: 0 - Clear, 1 - Detected"""
if device_type == "XMOSB01XS":
"""Translate to: occupancy: bool: 0 - Clear, 1 - Detected, 2 - Clear"""
device.update_predefined_binary_sensor(
BinarySensorDeviceClass.OCCUPANCY, xobj[0] == 1
)
else:
"""Translate to: occupancy: bool: 0 - Clear, 1 - Detected"""
device.update_predefined_binary_sensor(
BinarySensorDeviceClass.OCCUPANCY, xobj[0] > 0
)
return {}
def obj4851(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""From miot-spec: has-someone-duration: uint8: 2 - 2 minutes, 5 - 5 minutes"""
"""Translate to: duration_detected: uint8: 2 - 2 minutes, 5 - 5 minutes"""
device.update_sensor(
key=ExtendedSensorDeviceClass.DURATION_DETECTED,
name="Duration detected",
native_unit_of_measurement=Units.TIME_MINUTES,
device_class=ExtendedSensorDeviceClass.DURATION_DETECTED,
native_value=xobj[0],
)
return {}
def obj4852(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""From miot-spec: no-one-duration: uint8: 2/5/10/30 - 2/5/10/30 minutes"""
"""Translate to: duration_cleared: uint8: 2/5/10/30 - 2/5/10/30 minutes"""
device.update_sensor(
key=ExtendedSensorDeviceClass.DURATION_CLEARED,
name="Duration cleared",
native_unit_of_measurement=Units.TIME_MINUTES,
device_class=ExtendedSensorDeviceClass.DURATION_CLEARED,
native_value=xobj[0],
)
return {}
def obj4a01(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Low Battery"""
device.update_predefined_binary_sensor(BinarySensorDeviceClass.BATTERY, xobj[0])
return {}
def obj4a08(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Motion detected with Illuminance in lux"""
(illum,) = struct.unpack("f", xobj)
device.update_predefined_binary_sensor(BinarySensorDeviceClass.MOTION, True)
device.update_predefined_sensor(SensorLibrary.LIGHT__LIGHT_LUX, illum)
return {}
def obj4a0c(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Single press"""
if device_type == "XMWS01XS":
press = xobj[0]
if press == 0:
# left button
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_left",
event_type="press",
event_properties=None,
)
elif press == 1:
# right button
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_right",
event_type="press",
event_properties=None,
)
else:
device.fire_event(
key=EventDeviceKeys.BUTTON,
event_type="press",
event_properties=None,
)
return {}
def obj4a0d(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Double press"""
if device_type == "XMWS01XS":
press = xobj[0]
if press == 0:
# left button
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_left",
event_type="double_press",
event_properties=None,
)
elif press == 1:
# right button
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_right",
event_type="double_press",
event_properties=None,
)
else:
device.fire_event(
key=EventDeviceKeys.BUTTON,
event_type="double_press",
event_properties=None,
)
return {}
def obj4a0e(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Long press"""
if device_type == "XMWS01XS":
press = xobj[0]
if press == 0:
# left button
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_left",
event_type="long_press",
event_properties=None,
)
elif press == 1:
# right button
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_right",
event_type="long_press",
event_properties=None,
)
else:
device.fire_event(
key=EventDeviceKeys.BUTTON,
event_type="long_press",
event_properties=None,
)
return {}
def obj4a0f(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Device forcibly removed"""
dev_forced = xobj[0]
if dev_forced == 1:
device.update_predefined_binary_sensor(BinarySensorDeviceClass.OPENING, True)
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.DEVICE_FORCIBLY_REMOVED,
native_value=True,
device_class=ExtendedBinarySensorDeviceClass.DEVICE_FORCIBLY_REMOVED,
name="Device forcibly removed",
)
return {}
def obj4a12(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Opening (event)"""
opening_state = xobj[0]
# Opening event, used in combination with obj4804
if opening_state == 1:
device.update_predefined_binary_sensor(BinarySensorDeviceClass.OPENING, True)
elif opening_state == 2:
device.update_predefined_binary_sensor(BinarySensorDeviceClass.OPENING, False)
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.DOOR_LEFT_OPEN,
native_value=False,
device_class=ExtendedBinarySensorDeviceClass.DOOR_LEFT_OPEN,
name="Door left open",
)
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.DEVICE_FORCIBLY_REMOVED,
native_value=False, # reset device forcibly removed
device_class=ExtendedBinarySensorDeviceClass.DEVICE_FORCIBLY_REMOVED,
name="Device forcibly removed",
)
return {}
def obj4a13(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Button press (MS1BB(MI))"""
press = xobj[0]
if press == 1:
device.fire_event(
key=EventDeviceKeys.BUTTON,
event_type="press",
event_properties=None,
)
return {}
def obj4a1a(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Door left open"""
if xobj[0] == 1:
device.update_predefined_binary_sensor(BinarySensorDeviceClass.OPENING, True)
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.DOOR_LEFT_OPEN,
native_value=False,
device_class=ExtendedBinarySensorDeviceClass.DOOR_LEFT_OPEN,
name="Door left open",
)
return {}
def obj4c01(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Temperature"""
if len(xobj) == 4:
temp = FLOAT_STRUCT(xobj)[0]
device.update_predefined_sensor(
SensorLibrary.TEMPERATURE__CELSIUS, round(temp, 2)
)
return {}
def obj4c02(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Humidity"""
if len(xobj) == 1:
humi = xobj[0]
device.update_predefined_sensor(SensorLibrary.HUMIDITY__PERCENTAGE, humi)
return {}
def obj4c03(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Battery"""
device.update_predefined_sensor(SensorLibrary.BATTERY__PERCENTAGE, xobj[0])
return {}
def obj4c08(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Humidity"""
if len(xobj) == 4:
humi = FLOAT_STRUCT(xobj)[0]
device.update_predefined_sensor(SensorLibrary.HUMIDITY__PERCENTAGE, humi)
return {}
def obj4c14(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Mode"""
mode = xobj[0]
return {"mode": mode}
def obj4e01(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Low Battery"""
device.update_predefined_binary_sensor(BinarySensorDeviceClass.BATTERY, xobj[0])
return {}
def obj4e0c(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Button press"""
if device_type == "XMWXKG01YL":
press = xobj[0]
if press == 1:
# left button
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_left",
event_type="press",
event_properties=None,
)
elif press == 2:
# right button
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_right",
event_type="press",
event_properties=None,
)
elif press == 3:
# both left and right button
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_left",
event_type="press",
event_properties=None,
)
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_right",
event_type="press",
event_properties=None,
)
elif device_type == "K9BB-1BTN":
press = xobj[0]
if press == 1:
device.fire_event(
key=EventDeviceKeys.BUTTON,
event_type="press",
event_properties=None,
)
elif press == 8:
device.fire_event(
key=EventDeviceKeys.BUTTON,
event_type="long_press",
event_properties=None,
)
elif press == 15:
device.fire_event(
key=EventDeviceKeys.BUTTON,
event_type="double_press",
event_properties=None,
)
elif device_type == "XMWXKG01LM":
device.fire_event(
key=EventDeviceKeys.BUTTON,
event_type="press",
event_properties=None,
)
return {}
def obj4e0d(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Double Press"""
if device_type == "XMWXKG01YL":
press = xobj[0]
if press == 1:
# left button
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_left",
event_type="double_press",
event_properties=None,
)
elif press == 2:
# right button
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_right",
event_type="double_press",
event_properties=None,
)
elif press == 3:
# both left and right button
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_left",
event_type="double_press",
event_properties=None,
)
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_right",
event_type="double_press",
event_properties=None,
)
elif device_type == "XMWXKG01LM":
device.fire_event(
key=EventDeviceKeys.BUTTON,
event_type="double_press",
event_properties=None,
)
return {}
def obj4e0e(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Long Press"""
if device_type == "XMWXKG01YL":
press = xobj[0]
if press == 1:
# left button
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_left",
event_type="long_press",
event_properties=None,
)
elif press == 2:
# right button
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_right",
event_type="long_press",
event_properties=None,
)
elif press == 3:
# both left and right button
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_left",
event_type="long_press",
event_properties=None,
)
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_right",
event_type="long_press",
event_properties=None,
)
elif device_type == "XMWXKG01LM":
device.fire_event(
key=EventDeviceKeys.BUTTON,
event_type="long_press",
event_properties=None,
)
return {}
def obj4e1c(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Device reset"""
return {"device reset": True}
def obj5003(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Battery"""
device.update_predefined_sensor(SensorLibrary.BATTERY__PERCENTAGE, xobj[0])
return {}
def obj5413(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Customized Property"""
"""ES5BB customized-property-2 same as occupancy-status"""
if device_type not in ["ES5BB"]:
return {}
device.update_binary_sensor(
key=BinarySensorDeviceClass.OCCUPANCY + "_close_range",
native_value=xobj[0] > 0,
device_class=BinarySensorDeviceClass.OCCUPANCY,
name="Occupancy close range",
)
return {}
def obj5414(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Device mode (KSI and KSIBP, not used in HA)"""
return {"mode": xobj[0]}
def obj5422(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Charging State"""
if len(xobj) != 1:
return {}
if xobj[0] == 0:
charging_state = "Charging"
elif xobj[0] == 1:
charging_state = "Not Charging"
elif xobj[0] == 2:
charging_state = "Full"
else:
charging_state = "Unknown"
device.update_sensor(
key=ExtendedSensorDeviceClass.CHARGING_STATE,
device_class=ExtendedSensorDeviceClass.CHARGING_STATE,
name="Charging State",
native_value=charging_state,
native_unit_of_measurement=None,
)
return {}
def obj560c(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Button press"""
if device_type not in ["KS1", "KS1BP", "KS2BB"]:
return {}
if device_type == "KS2BB":
button_key = EventDeviceKeys.BUTTON
else:
button = xobj[0]
button_name = QUAD_BUTTON_TO_NAME.get(button)
button_key = (
f"{str(EventDeviceKeys.BUTTON)}_{button_name}"
if button_name
else EventDeviceKeys.BUTTON
)
device.fire_event(
key=button_key,
event_type="press",
event_properties=None,
)
return {}
def obj560d(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Double button press"""
if device_type not in ["KS1", "KS1BP", "KS2BB"]:
return {}
if device_type == "KS2BB":
device.fire_event(
key=EventDeviceKeys.BUTTON,
event_type="double_press",
event_properties=None,
)
return {}
button = xobj[0]
if button_name := QUAD_BUTTON_TO_NAME[button]:
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_{button_name}",
event_type="double_press",
event_properties=None,
)
return {}
def obj560e(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Long button press"""
if device_type not in ["KS1", "KS1BP", "KS2BB"]:
return {}
if device_type == "KS2BB":
device.fire_event(
key=EventDeviceKeys.BUTTON,
event_type="long_press",
event_properties=None,
)
return {}
button = xobj[0]
if button_name := QUAD_BUTTON_TO_NAME[button]:
device.fire_event(
key=f"{str(EventDeviceKeys.BUTTON)}_{button_name}",
event_type="long_press",
event_properties=None,
)
return {}
def obj4e16(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""
Parser for Xiaomi Smart Scale S200 MJTZC02YM
https://www.mi.com/global/product/xiaomi-smart-scale-s200/
"""
if len(xobj) != 9:
return {}
profile_id, data, timestamp = struct.unpack(" dict[str, Any]:
"""Sleep State"""
if len(xobj) != 1:
return {}
sleep_state = xobj[0] ^ 1
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.ASLEEP,
native_value=sleep_state,
device_class=ExtendedBinarySensorDeviceClass.ASLEEP,
name="Asleep",
)
return {}
def obj6461(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Wearing State"""
if len(xobj) != 1:
return {}
wearing_state = xobj[0] ^ 1
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.WEARING,
native_value=wearing_state,
device_class=ExtendedBinarySensorDeviceClass.WEARING,
name="Wearing",
)
return {}
def obj6e16(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Body Composition Scale S400"""
profile_id, data, _ = struct.unpack("> 11) & 0x7F
impedance = data >> 18
device.update_sensor(
key=ExtendedSensorDeviceClass.PROFILE_ID,
name="Profile ID",
device_class=ExtendedSensorDeviceClass.PROFILE_ID,
native_unit_of_measurement=None,
native_value=profile_id,
)
if mass == 0 and heart_rate == 0 and impedance == 0:
# Person stepped off the scale → reset
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.STABILIZED,
name="Stabilized",
device_class=ExtendedBinarySensorDeviceClass.STABILIZED,
native_value=False,
)
return {}
if mass != 0:
device.update_predefined_sensor(SensorLibrary.MASS__MASS_KILOGRAMS, mass / 10)
if 0 < heart_rate < 127:
device.update_sensor(
key=ExtendedSensorDeviceClass.HEART_RATE,
name="Heart Rate",
device_class=ExtendedSensorDeviceClass.HEART_RATE,
native_unit_of_measurement="bpm",
native_value=heart_rate + 50,
)
if mass == 0 and heart_rate == 0:
# Last packet → high frequency 250 kHz → impedance_high → stabilized
device.update_sensor(
key=ExtendedSensorDeviceClass.IMPEDANCE_HIGH,
name="Impedance High",
device_class=ExtendedSensorDeviceClass.IMPEDANCE_HIGH,
native_unit_of_measurement=Units.OHM,
native_value=impedance / 10,
)
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.STABILIZED,
name="Stabilized",
device_class=ExtendedBinarySensorDeviceClass.STABILIZED,
native_value=True,
)
elif impedance != 0:
# Packet with weight → low frequency 50 kHz → impedance_low
device.update_sensor(
key=ExtendedSensorDeviceClass.IMPEDANCE_LOW,
name="Impedance Low",
device_class=ExtendedSensorDeviceClass.IMPEDANCE_LOW,
native_unit_of_measurement=Units.OHM,
native_value=impedance / 10,
)
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.STABILIZED,
name="Stabilized",
device_class=ExtendedBinarySensorDeviceClass.STABILIZED,
native_value=False,
)
else:
# Packet with weight only → with socks → stabilized
device.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.STABILIZED,
name="Stabilized",
device_class=ExtendedBinarySensorDeviceClass.STABILIZED,
native_value=True,
)
return {}
# Dataobject dictionary
# {dataObject_id: (converter}
xiaomi_dataobject_dict = {
0x0003: obj0003,
0x0006: obj0006,
0x0007: obj0007,
0x0008: obj0008,
0x0010: obj0010,
0x000A: obj000a,
0x000B: obj000b,
0x000F: obj000f,
0x1001: obj1001,
0x1004: obj1004,
0x1005: obj1005,
0x1006: obj1006,
0x1007: obj1007,
0x1008: obj1008,
0x1009: obj1009,
0x1010: obj1010,
0x1012: obj1012,
0x1013: obj1013,
0x1014: obj1014,
0x1015: obj1015,
0x1017: obj1017,
0x1018: obj1018,
0x1019: obj1019,
0x100A: obj100a,
0x100D: obj100d,
0x100E: obj100e,
0x101B: obj101b,
0x2000: obj2000,
0x3003: obj3003,
0x4801: obj4801,
0x4802: obj4802,
0x4803: obj4803,
0x4804: obj4804,
0x4805: obj4805,
0x4806: obj4806,
0x4808: obj4808,
0x480C: obj480c,
0x4818: obj4818,
0x483C: obj483c,
0x483D: obj483d,
0x483E: obj483e,
0x484E: obj484e,
0x4851: obj4851,
0x4852: obj4852,
0x4A01: obj4a01,
0x4A08: obj4a08,
0x4A0C: obj4a0c,
0x4A0D: obj4a0d,
0x4A0E: obj4a0e,
0x4A0F: obj4a0f,
0x4A12: obj4a12,
0x4A13: obj4a13,
0x4A1A: obj4a1a,
0x4C01: obj4c01,
0x4C02: obj4c02,
0x4C03: obj4c03,
0x4C08: obj4c08,
0x4C14: obj4c14,
0x4E01: obj4e01,
0x4E16: obj4e16,
0x4E1C: obj4e1c,
0x4E0C: obj4e0c,
0x4E0D: obj4e0d,
0x4E0E: obj4e0e,
0x5003: obj5003,
0x5413: obj5413,
0x5414: obj5414,
0x5422: obj5422,
0x560C: obj560c,
0x560D: obj560d,
0x560E: obj560e,
0x5810: obj5810,
0x6461: obj6461,
0x6E16: obj6e16,
}
def decode_temps(packet_value: int) -> float:
"""Decode potential negative temperatures."""
# https://github.com/Thrilleratplay/XiaomiWatcher/issues/2
if packet_value & 0x800000:
return float((packet_value ^ 0x800000) / -10000)
return float(packet_value / 10000)
def decode_temps_probes(packet_value: int) -> float:
"""Filter potential negative temperatures."""
if packet_value < 0:
return 0.0
return float(packet_value / 100)
class XiaomiBluetoothDeviceData(BluetoothData):
"""Data for Xiaomi BLE sensors."""
def __init__(self, bindkey: bytes | None = None) -> None:
super().__init__()
self.set_bindkey(bindkey)
# Data that we know how to parse but don't yet map to the SensorData model.
self.unhandled: dict[str, Any] = {}
# The type of encryption to expect, based on flags in the bluetooth
# frame.
self.encryption_scheme = EncryptionScheme.NONE
# If true then we have used the provided encryption key to decrypt at least
# one payload.
# If false then we have either not seen an encrypted payload, the key is wrong
# or encryption is not in use
self.bindkey_verified = False
# If True then the decryption has failed or has not been verified yet.
# If False then the decryption has succeeded.
self.decryption_failed = True
# If this is True, then we have not seen an advertisement with a payload
# Until we see a payload, we can't tell if this device is encrypted or not
self.pending = True
# The last service_info we saw that had a payload
# We keep this to help in reauth flows where we want to reprocess and old
# value with a new bindkey.
self.last_service_info: BluetoothServiceInfo | None = None
# If this is True, the device is not sending advertisements
# in a regular interval
self.sleepy_device = False
def set_bindkey(self, bindkey: bytes | None) -> None:
"""Set the bindkey."""
if bindkey:
if len(bindkey) == 12:
# MiBeacon v2/v3 bindkey (requires 4 additional (fixed) bytes)
bindkey = b"".join(
[bindkey[0:6], bytes.fromhex("8d3d3c97"), bindkey[6:]]
)
elif len(bindkey) == 16:
self.cipher: AESCCM | None = AESCCM(bindkey, tag_length=4)
else:
self.cipher = None
self.bindkey = bindkey
def supported(self, data: BluetoothServiceInfo) -> bool:
if not super().supported(data):
return False
return True
def _start_update(self, service_info: BluetoothServiceInfo) -> None:
"""Update from BLE advertisement data."""
_LOGGER.debug("Parsing Xiaomi BLE advertisement data: %s", service_info)
for uuid, data in service_info.service_data.items():
if uuid == SERVICE_MIBEACON:
if self._parse_xiaomi(service_info, service_info.name, data):
self.last_service_info = service_info
elif uuid == SERVICE_HHCCJCY10:
if self._parse_hhcc(service_info, data):
self.last_service_info = service_info
elif uuid == SERVICE_SCALE1:
if self._parse_scale_v1(service_info, data):
self.last_service_info = service_info
elif uuid == SERVICE_SCALE2:
if self._parse_scale_v2(service_info, data):
self.last_service_info = service_info
def _parse_hhcc(self, service_info: BluetoothServiceInfo, data: bytes) -> bool:
"""Parser for Pink version of HHCCJCY10."""
if len(data) != 9:
return False
identifier = short_address(service_info.address)
self.set_title(f"Plant Sensor {identifier} (HHCCJCY10)")
self.set_device_name(f"Plant Sensor {identifier}")
self.set_device_type("HHCCJCY10")
self.set_device_manufacturer("HHCC Plant Technology Co. Ltd")
xvalue_1 = data[0:3]
moist, temp = struct.unpack(">BH", xvalue_1)
self.update_predefined_sensor(SensorLibrary.TEMPERATURE__CELSIUS, temp / 10)
self.update_predefined_sensor(SensorLibrary.MOISTURE__PERCENTAGE, moist)
xvalue_2 = data[3:6]
(illu,) = struct.unpack(">i", b"\x00" + xvalue_2)
self.update_predefined_sensor(SensorLibrary.LIGHT__LIGHT_LUX, illu)
xvalue_3 = data[6:9]
batt, cond = struct.unpack(">BH", xvalue_3)
self.update_predefined_sensor(SensorLibrary.BATTERY__PERCENTAGE, batt)
self.update_predefined_sensor(SensorLibrary.CONDUCTIVITY__CONDUCTIVITY, cond)
return True
def _parse_xiaomi(
self, service_info: BluetoothServiceInfo, name: str, data: bytes
) -> bool:
"""Parser for Xiaomi sensors"""
# check for adstruc length
i = 5 # till Frame Counter
msg_length = len(data)
if msg_length < i:
_LOGGER.debug("Invalid data length (initial check), adv: %s", data.hex())
return False
mac_readable = service_info.address
source_mac = bytes.fromhex(mac_readable.replace(":", ""))
# extract frame control bits
frctrl = data[0] + (data[1] << 8)
frctrl_mesh = (frctrl >> 7) & 1 # mesh device
frctrl_version = frctrl >> 12 # version
frctrl_auth_mode = (frctrl >> 10) & 3
frctrl_solicited = (frctrl >> 9) & 1
frctrl_registered = (frctrl >> 8) & 1
frctrl_object_include = (frctrl >> 6) & 1
frctrl_capability_include = (frctrl >> 5) & 1
frctrl_mac_include = (frctrl >> 4) & 1 # check for MAC address in data
frctrl_is_encrypted = (frctrl >> 3) & 1 # check for encryption being used
frctrl_request_timing = frctrl & 1 # old version
# Check that device is not of mesh type
if frctrl_mesh != 0:
_LOGGER.debug(
"Device is a mesh type device, which is not supported. Data: %s",
data.hex(),
)
return False
# Check that version is 2 or higher
if frctrl_version < 2:
_LOGGER.debug(
"Device is using old data format, which is not supported. Data: %s",
data.hex(),
)
return False
# Check that MAC in data is the same as the source MAC
if frctrl_mac_include != 0:
i += 6
if msg_length < i:
_LOGGER.debug("Invalid data length (in MAC check), adv: %s", data.hex())
return False
xiaomi_mac_reversed = data[5:11]
xiaomi_mac = xiaomi_mac_reversed[::-1]
if xiaomi_mac != source_mac:
_LOGGER.debug(
"MAC address doesn't match data frame. Expected: %s, Got: %s",
to_mac(xiaomi_mac),
to_mac(source_mac),
)
return False
else:
xiaomi_mac = source_mac
# determine the device type
device_id = data[2] + (data[3] << 8)
try:
device = DEVICE_TYPES[device_id]
except KeyError:
_LOGGER.info(
"BLE ADV from UNKNOWN Xiaomi device: MAC: %s, product_id: %s, ADV: %s",
to_mac(source_mac),
hex(device_id),
data.hex(),
)
_LOGGER.debug(
"Unknown Xiaomi device found. product_id: %s, Data: %s",
hex(device_id),
data.hex(),
)
return False
device_type = device.model
self.device_id = device_id
self.device_type = device_type
# set to True if the device is not sending regular BLE advertisements
self.sleepy_device = device_type in SLEEPY_DEVICE_MODELS
packet_id = data[4]
sinfo = "MiVer: " + str(frctrl_version)
sinfo += ", DevID: " + hex(device_id) + " : " + device_type
sinfo += ", FnCnt: " + str(packet_id)
if frctrl_request_timing != 0:
sinfo += ", Request timing"
if frctrl_registered != 0:
sinfo += ", Registered and bound"
else:
sinfo += ", Not bound"
if frctrl_solicited != 0:
sinfo += ", Request APP to register and bind"
if frctrl_auth_mode == 0:
sinfo += ", Old version certification"
elif frctrl_auth_mode == 1:
sinfo += ", Safety certification"
elif frctrl_auth_mode == 2:
sinfo += ", Standard certification"
# check for capability byte present
if frctrl_capability_include != 0:
i += 1
if msg_length < i:
_LOGGER.debug(
"Invalid data length (in capability check), adv: %s", data.hex()
)
return False
capability_types = data[i - 1]
sinfo += ", Capability: " + hex(capability_types)
if (capability_types & 0x20) != 0:
i += 1
if msg_length < i:
_LOGGER.debug(
"Invalid data length (in capability type check), adv: %s",
data.hex(),
)
return False
capability_io = data[i - 1]
sinfo += ", IO: " + hex(capability_io)
identifier = short_address(service_info.address)
self.set_title(f"{device.name} {identifier} ({device.model})")
self.set_device_name(f"{device.name} {identifier}")
self.set_device_type(device.model)
self.set_device_manufacturer(device.manufacturer)
# check that data contains object
if frctrl_object_include == 0:
# data does not contain Object
_LOGGER.debug("Advertisement doesn't contain payload, adv: %s", data.hex())
# S400 idle packet — reset stabilized
if device_type in S400_MODELS:
self.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.STABILIZED,
name="Stabilized",
device_class=ExtendedBinarySensorDeviceClass.STABILIZED,
native_value=False,
)
return False
self.pending = False
# check for encryption
if frctrl_is_encrypted != 0:
sinfo += ", Encryption"
firmware = "Xiaomi (MiBeacon V" + str(frctrl_version) + " encrypted)"
if frctrl_version <= 3:
self.encryption_scheme = EncryptionScheme.MIBEACON_LEGACY
payload = self._decrypt_mibeacon_legacy(data, i, xiaomi_mac)
else:
self.encryption_scheme = EncryptionScheme.MIBEACON_4_5
payload = self._decrypt_mibeacon_v4_v5(data, i, xiaomi_mac)
else: # No encryption
# check minimum advertisement length with data
firmware = "Xiaomi (MiBeacon V" + str(frctrl_version) + ")"
sinfo += ", No encryption"
if msg_length < i + 3:
_LOGGER.debug(
"Invalid data length (in non-encrypted data), adv: %s",
data.hex(),
)
return False
payload = data[i:]
self.set_device_sw_version(firmware)
if payload is not None:
sinfo += ", Object data: " + payload.hex()
# loop through parse_xiaomi payload
payload_start = 0
payload_length = len(payload)
# assume that the data may have several values of different types
while payload_length >= payload_start + 3:
obj_typecode = payload[payload_start] + (
payload[payload_start + 1] << 8
)
obj_length = payload[payload_start + 2]
next_start = payload_start + 3 + obj_length
if payload_length < next_start:
# The payload segments are corrupted - if this is legacy encryption
# then the key is probably just wrong
# V4/V5 encryption has an authentication tag, so we don't apply the
# same restriction there.
if self.encryption_scheme == EncryptionScheme.MIBEACON_LEGACY:
if self.decryption_failed is True:
# we only ask for reautentification
# till the decryption has failed twice.
self.bindkey_verified = False
else:
self.decryption_failed = True
_LOGGER.debug(
"Invalid payload data length, payload: %s", payload.hex()
)
break
this_start = payload_start + 3
dobject = payload[this_start:next_start]
if (
dobject
and obj_length != 0
or hex(obj_typecode) in OBJECTS_DEVICE_TYPE
):
resfunc = xiaomi_dataobject_dict.get(obj_typecode, None)
if resfunc:
self.unhandled.update(resfunc(dobject, self, device_type))
else:
_LOGGER.info(
"%s, UNKNOWN dataobject in payload! Adv: %s",
sinfo,
data.hex(),
)
payload_start = next_start
return True
def _parse_scale_v1(self, service_info: BluetoothServiceInfo, data: bytes) -> bool:
if len(data) != 10:
return False
uuid16 = (data[3] << 8) | data[2]
identifier = short_address(service_info.address)
self.device_id = uuid16
self.set_title(f"Mi Smart Scale ({identifier})")
self.set_device_name(f"Mi Smart Scale ({identifier})")
self.set_device_type("XMTZC01HM/XMTZC04HM")
self.set_device_manufacturer("Xiaomi")
self.pending = False
self.sleepy_device = True
control_byte = data[0]
mass = float(int.from_bytes(data[1:3], byteorder="little"))
mass_in_pounds = bool(int(control_byte & (1 << 0)))
mass_in_catty = bool(int(control_byte & (1 << 4)))
mass_in_kilograms = not mass_in_catty and not mass_in_pounds
mass_stabilized = bool(int(control_byte & (1 << 5)))
mass_removed = bool(int(control_byte & (1 << 7)))
# Reset stabilization state when scale is empty / user steps off
if mass == 0:
self.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.STABILIZED,
name="Stabilized",
device_class=ExtendedBinarySensorDeviceClass.STABILIZED,
native_value=False,
)
if mass_in_kilograms:
# sensor advertises kg * 200
mass /= 200
elif mass_in_pounds:
# sensor advertises lbs * 100, conversion to kg (1 lbs = 0.45359237 kg)
mass *= 0.0045359237
else:
# sensor advertises catty * 100, conversion to kg (1 catty = 0.5 kg)
mass *= 0.005
self.update_predefined_sensor(
SensorLibrary.MASS_NON_STABILIZED__MASS_KILOGRAMS, mass
)
# Handling stabilization sensor based on weight status
if mass_stabilized and not mass_removed and mass > 0:
self.update_predefined_sensor(SensorLibrary.MASS__MASS_KILOGRAMS, mass)
self.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.STABILIZED,
name="Stabilized",
device_class=ExtendedBinarySensorDeviceClass.STABILIZED,
native_value=True,
)
else:
# Scale is measuring / not stabilized yet
if mass > 0:
self.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.STABILIZED,
name="Stabilized",
device_class=ExtendedBinarySensorDeviceClass.STABILIZED,
native_value=False,
)
return True
def _parse_scale_v2(self, service_info: BluetoothServiceInfo, data: bytes) -> bool:
if len(data) != 13:
return False
uuid16 = (data[3] << 8) | data[2]
identifier = short_address(service_info.address)
self.device_id = uuid16
self.set_title(f"Mi Body Composition Scale ({identifier})")
self.set_device_name(f"Mi Body Composition Scale ({identifier})")
self.set_device_type("XMTZC02HM/XMTZC05HM/NUN4049CN")
self.set_device_manufacturer("Xiaomi")
self.pending = False
self.sleepy_device = True
control_bytes = data[:2]
# skip bytes containing date and time
impedance = int.from_bytes(data[9:11], byteorder="little")
mass = float(int.from_bytes(data[11:13], byteorder="little"))
# Decode control bytes
control_flags = "".join([bin(byte)[2:].zfill(8) for byte in control_bytes])
mass_in_pounds = bool(int(control_flags[7]))
mass_in_catty = bool(int(control_flags[9]))
mass_in_kilograms = not mass_in_catty and not mass_in_pounds
mass_stabilized = bool(int(control_flags[10]))
mass_removed = bool(int(control_flags[8]))
impedance_stabilized = bool(int(control_flags[14]))
# Reset stabilization state when scale is empty / user steps off
if mass == 0:
self.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.STABILIZED,
name="Stabilized",
device_class=ExtendedBinarySensorDeviceClass.STABILIZED,
native_value=False,
)
if mass_in_kilograms:
# sensor advertises kg * 200
mass /= 200
elif mass_in_pounds:
# sensor advertises lbs * 100, conversion to kg (1 lbs = 0.45359237 kg)
mass *= 0.0045359237
else:
# sensor advertises catty * 100, conversion to kg (1 catty = 0.5 kg)
mass *= 0.005
self.update_predefined_sensor(
SensorLibrary.MASS_NON_STABILIZED__MASS_KILOGRAMS, mass
)
# Handling stabilization sensor based on weight status
if mass_stabilized and not mass_removed and mass > 0:
self.update_predefined_sensor(SensorLibrary.MASS__MASS_KILOGRAMS, mass)
if impedance_stabilized:
self.update_predefined_sensor(SensorLibrary.IMPEDANCE__OHM, impedance)
self.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.STABILIZED,
name="Stabilized",
device_class=ExtendedBinarySensorDeviceClass.STABILIZED,
native_value=True,
)
else:
# Scale is measuring / not stabilized yet
if mass > 0:
self.update_binary_sensor(
key=ExtendedBinarySensorDeviceClass.STABILIZED,
name="Stabilized",
device_class=ExtendedBinarySensorDeviceClass.STABILIZED,
native_value=False,
)
return True
def _decrypt_mibeacon_v4_v5(
self, data: bytes, i: int, xiaomi_mac: bytes
) -> bytes | None:
"""decrypt MiBeacon v4/v5 encrypted advertisements"""
# check for minimum length of encrypted advertisement
if len(data) < i + 9:
_LOGGER.debug("Invalid data length (for decryption), adv: %s", data.hex())
return None
if not self.bindkey:
self.bindkey_verified = False
_LOGGER.debug("Encryption key not set and adv is encrypted")
return None
if not self.bindkey or len(self.bindkey) != 16:
self.bindkey_verified = False
_LOGGER.error("Encryption key should be 16 bytes (32 characters) long")
return None
nonce = b"".join([xiaomi_mac[::-1], data[2:5], data[-7:-4]])
associated_data = b"\x11"
mic = data[-4:]
encrypted_payload = data[i:-7]
assert self.cipher is not None # nosec
# decrypt the data
try:
decrypted_payload = self.cipher.decrypt(
nonce, encrypted_payload + mic, associated_data
)
except InvalidTag as error:
if self.decryption_failed is True:
# we only ask for reautentification till
# the decryption has failed twice.
self.bindkey_verified = False
else:
self.decryption_failed = True
_LOGGER.warning("Decryption failed: %s", error)
_LOGGER.debug("mic: %s", mic.hex())
_LOGGER.debug("nonce: %s", nonce.hex())
_LOGGER.debug("encrypted payload: %s", encrypted_payload.hex())
return None
if decrypted_payload is None:
self.bindkey_verified = False
_LOGGER.error(
"Decryption failed for %s, decrypted payload is None",
to_mac(xiaomi_mac),
)
return None
self.decryption_failed = False
self.bindkey_verified = True
return decrypted_payload
def _decrypt_mibeacon_legacy(
self, data: bytes, i: int, xiaomi_mac: bytes
) -> bytes | None:
"""decrypt MiBeacon v2/v3 encrypted advertisements"""
# check for minimum length of encrypted advertisement
if len(data) < i + 7:
_LOGGER.debug("Invalid data length (for decryption), adv: %s", data.hex())
return None
if not self.bindkey:
self.bindkey_verified = False
_LOGGER.debug("Encryption key not set and adv is encrypted")
return None
if len(self.bindkey) != 16:
self.bindkey_verified = False
_LOGGER.error("Encryption key should be 12 bytes (24 characters) long")
return None
nonce = b"".join([data[0:5], data[-4:-1], xiaomi_mac[::-1][:-1]])
encrypted_payload = data[i:-4]
# cryptography can't decrypt a message without authentication
# so we have to use Cryptodome
associated_data = b"\x11"
cipher = AES.new(self.bindkey, AES.MODE_CCM, nonce=nonce, mac_len=4)
cipher.update(associated_data)
assert cipher is not None # nosec
# decrypt the data
# note that V2/V3 encryption will often pass the decryption process with a
# wrong encryption key, resulting in useless data, and we won't be able
# to verify this, as V2/V3 encryption does not use a tag to verify
# the decrypted data. This will be filtered as wrong data length
# during the conversion of the payload to sensor data.
try:
decrypted_payload = cipher.decrypt(encrypted_payload)
except ValueError as error:
self.bindkey_verified = False
_LOGGER.warning("Decryption failed: %s", error)
_LOGGER.debug("nonce: %s", nonce.hex())
_LOGGER.debug("encrypted payload: %s", encrypted_payload.hex())
return None
if decrypted_payload is None:
self.bindkey_verified = False
_LOGGER.warning(
"Decryption failed for %s, decrypted payload is None",
to_mac(xiaomi_mac),
)
return None
self.bindkey_verified = True
return decrypted_payload
def poll_needed(
self, service_info: BluetoothServiceInfo, last_poll: float | None
) -> bool:
"""
This is called every time we get a service_info for a device. It means the
device is working and online. If 24 hours has passed, it may be a good
time to poll the device.
"""
if self.pending:
# Never need to poll if we are pending as we don't even know what
# kind of device we are
return False
if self.device_id not in [0x03BC, 0x0098]:
return False
return not last_poll or last_poll > TIMEOUT_1DAY
async def async_poll(self, ble_device: BLEDevice) -> SensorUpdate:
"""
Poll the device to retrieve any values we can't get from passive listening.
"""
if self.device_id in [0x03BC, 0x0098]:
client = await establish_connection(
BleakClient, ble_device, ble_device.address
)
try:
battery_char = client.services.get_characteristic(
CHARACTERISTIC_BATTERY
)
payload = await client.read_gatt_char(battery_char)
finally:
await client.disconnect()
self.set_device_sw_version(payload[2:].decode("utf-8"))
self.update_predefined_sensor(SensorLibrary.BATTERY__PERCENTAGE, payload[0])
return self._finish_update()
Bluetooth-Devices-xiaomi-ble-741c848/src/xiaomi_ble/py.typed 0000664 0000000 0000000 00000000000 15222637367 0023750 0 ustar 00root root 0000000 0000000 Bluetooth-Devices-xiaomi-ble-741c848/tests/ 0000775 0000000 0000000 00000000000 15222637367 0020526 5 ustar 00root root 0000000 0000000 Bluetooth-Devices-xiaomi-ble-741c848/tests/__init__.py 0000664 0000000 0000000 00000000000 15222637367 0022625 0 ustar 00root root 0000000 0000000 Bluetooth-Devices-xiaomi-ble-741c848/tests/test_parser.py 0000664 0000000 0000000 00000637327 15222637367 0023455 0 ustar 00root root 0000000 0000000 """The tests for the Xiaomi ble parser."""
import logging
from unittest.mock import patch
import pytest
from home_assistant_bluetooth import BluetoothServiceInfo
from sensor_state_data import (
BinarySensorDescription,
BinarySensorDeviceClass,
BinarySensorValue,
DeviceClass,
DeviceKey,
Event,
SensorDescription,
SensorDeviceInfo,
SensorUpdate,
SensorValue,
Units,
)
from xiaomi_ble.const import SERVICE_HHCCJCY10, SERVICE_SCALE1, SERVICE_SCALE2
from xiaomi_ble.parser import (
EncryptionScheme,
ExtendedBinarySensorDeviceClass,
ExtendedSensorDeviceClass,
XiaomiBluetoothDeviceData,
)
KEY_BATTERY = DeviceKey(key="battery", device_id=None)
KEY_BINARY_DOOR = DeviceKey(key="door", device_id=None)
KEY_BINARY_FINGERPRINT = DeviceKey(key="fingerprint", device_id=None)
KEY_BINARY_MOTION = DeviceKey(key="motion", device_id=None)
KEY_BINARY_OCCUPANCY = DeviceKey(key="occupancy", device_id=None)
KEY_BINARY_OCCUPANCY_CLOSE_RANGE = DeviceKey(
key="occupancy_close_range", device_id=None
)
KEY_BINARY_LIGHT = DeviceKey(key="light", device_id=None)
KEY_BINARY_LOCK = DeviceKey(key="lock", device_id=None)
KEY_BINARY_OPENING = DeviceKey(key="opening", device_id=None)
KEY_BINARY_DOOR_LEFT_OPEN = DeviceKey(key="door_left_open", device_id=None)
KEY_BINARY_DEVICE_FORCIBLY_REMOVED = DeviceKey(
key="device_forcibly_removed", device_id=None
)
KEY_BINARY_PRY_THE_DOOR = DeviceKey(key="pry_the_door", device_id=None)
KEY_BINARY_TOOTHBRUSH = DeviceKey(key="toothbrush", device_id=None)
KEY_CONDUCTIVITY = DeviceKey(key="conductivity", device_id=None)
KEY_COUNTER = DeviceKey(key="counter", device_id=None)
KEY_EVENT_BUTTON = DeviceKey(key="button", device_id=None)
KEY_EVENT_BUTTON_LEFT = DeviceKey(key="button_left", device_id=None)
KEY_EVENT_CUBE = DeviceKey(key="cube", device_id=None)
KEY_EVENT_DIMMER = DeviceKey(key="dimmer", device_id=None)
KEY_EVENT_FINGERPRINT = DeviceKey(key="fingerprint", device_id=None)
KEY_EVENT_MOTION = DeviceKey(key="motion", device_id=None)
KEY_HUMIDITY = DeviceKey(key="humidity", device_id=None)
KEY_ILLUMINANCE = DeviceKey(key="illuminance", device_id=None)
KEY_IMPEDANCE = DeviceKey(key="impedance", device_id=None)
KEY_KEY_ID = DeviceKey(key="key_id", device_id=None)
KEY_LOCK_METHOD = DeviceKey(key="lock_method", device_id=None)
KEY_MASS_NON_STABILIZED = DeviceKey(key="mass_non_stabilized", device_id=None)
KEY_MASS = DeviceKey(key="mass", device_id=None)
KEY_MOISTURE = DeviceKey(key="moisture", device_id=None)
KEY_POWER = DeviceKey(key="power", device_id=None)
KEY_SCORE = DeviceKey(key="score", device_id=None)
KEY_SIGNAL_STRENGTH = DeviceKey(key="signal_strength", device_id=None)
KEY_SMOKE = DeviceKey(key="smoke", device_id=None)
KEY_TEMPERATURE = DeviceKey(key="temperature", device_id=None)
KEY_IMPEDANCE_LOW = DeviceKey(key="impedance_low", device_id=None)
KEY_IMPEDANCE_HIGH = DeviceKey(key="impedance_high", device_id=None)
KEY_HEART_RATE = DeviceKey(key="heart_rate", device_id=None)
KEY_PROFILE_ID = DeviceKey(key="profile_id", device_id=None)
KEY_STABILIZED = DeviceKey(key="stabilized", device_id=None)
KEY_TIMESTAMP = DeviceKey(key="timestamp", device_id=None)
KEY_VOLTAGE = DeviceKey(key="voltage", device_id=None)
KEY_CHARGING_STATE = DeviceKey(key="charging_state", device_id=None)
KEY_ASLEEP = DeviceKey(key="asleep", device_id=None)
KEY_WEARING = DeviceKey(key="wearing", device_id=None)
KEY_DURATION_DETECTED = DeviceKey(key="duration_detected", device_id=None)
KEY_DURATION_CLEARED = DeviceKey(key="duration_cleared", device_id=None)
KEY_PRESSURE_PRESENT_DURATION = DeviceKey(
key="pressure_present_duration", device_id=None
)
KEY_PRESSURE_NOT_PRESENT_DURATION = DeviceKey(
key="pressure_not_present_duration", device_id=None
)
@pytest.fixture(autouse=True)
def logging_config(caplog):
caplog.set_level(logging.DEBUG)
@pytest.fixture(autouse=True)
def mock_platform():
with patch("sys.platform") as p:
p.return_value = "linux"
yield p
def bytes_to_service_info(
payload: bytes, address: str = "00:00:00:00:00:00"
) -> BluetoothServiceInfo:
return BluetoothServiceInfo(
name="Test",
address=address,
rssi=-60,
manufacturer_data={},
service_data={"0000fe95-0000-1000-8000-00805f9b34fb": payload},
service_uuids=["0000fe95-0000-1000-8000-00805f9b34fb"],
source="",
)
def test_blank_advertisements_then_encrypted():
"""Test that we can reject empty payloads."""
device = XiaomiBluetoothDeviceData()
# First advertisement has a header but no payload, so we can't tell
# if it has encryption
data_string = b"0X[\x05\x02H<\xd48\xc1\xa4\x08"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:D4:3C:48")
assert device.supported(advertisement)
assert device.encryption_scheme == EncryptionScheme.NONE
assert device.pending is True
assert device.last_service_info is None
# Second advertisement has encryption
data_string = b"XX[\x05\x01H<\xd48\xc1\xa4\x9c\xf2U\xcf\xdd\x00\x00\x00/\xae/\xf2"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:D4:3C:48")
device.update(advertisement)
assert device.encryption_scheme == EncryptionScheme.MIBEACON_4_5
assert device.pending is False # type: ignore
def test_blank_advertisements_then_unencrypted():
"""Test that we can reject empty payloads."""
# NOTE: THIS IS SYNTHETIC TEST DATA - i took a known unencrypted device and flipped
# frctrl_object_include, then truncated it to not include the data payload
device = XiaomiBluetoothDeviceData()
# First advertisement has a header but no payload, so we can't tell
# if it has encryption
data_string = b"1 \x98\x00\x12\xf3Ok\x8d|\xc4\r"
advertisement = bytes_to_service_info(data_string, address="C4:7C:8D:6B:4F:F3")
assert device.supported(advertisement)
assert device.encryption_scheme == EncryptionScheme.NONE
assert device.pending is True
# Second advertisement has encryption
data_string = b"q \x98\x00\x12\xf3Ok\x8d|\xc4\r\x04\x10\x02\xc4\x00"
advertisement = bytes_to_service_info(data_string, address="C4:7C:8D:6B:4F:F3")
device.update(advertisement)
assert device.encryption_scheme == EncryptionScheme.NONE
assert device.pending is False
def test_blank_advertisements_then_encrypted_last_service_info():
"""Test that we can capture valid service info records"""
device = XiaomiBluetoothDeviceData()
# First advertisement has a header but no payload, so we can't tell
# if it has encryption
data_string = b"0X[\x05\x02H<\xd48\xc1\xa4\x08"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:D4:3C:48")
assert device.supported(advertisement)
assert device.last_service_info is None
# Second advertisement has encryption
data_string = b"XX[\x05\x01H<\xd48\xc1\xa4\x9c\xf2U\xcf\xdd\x00\x00\x00/\xae/\xf2"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:D4:3C:48")
device.update(advertisement)
assert device.last_service_info == advertisement
def test_blank_advertisements_then_unencrypted_last_service_info():
"""Test that we can capture valid service info records."""
# NOTE: THIS IS SYNTHETIC TEST DATA - i took a known unencrypted device and flipped
# frctrl_object_include, then truncated it to not include the data payload
device = XiaomiBluetoothDeviceData()
# First advertisement has a header but no payload, so we can't tell
# if it has encryption
data_string = b"1 \x98\x00\x12\xf3Ok\x8d|\xc4\r"
advertisement = bytes_to_service_info(data_string, address="C4:7C:8D:6B:4F:F3")
assert device.supported(advertisement)
assert device.last_service_info is None
# Second advertisement has encryption
data_string = b"q \x98\x00\x12\xf3Ok\x8d|\xc4\r\x04\x10\x02\xc4\x00"
advertisement = bytes_to_service_info(data_string, address="C4:7C:8D:6B:4F:F3")
device.update(advertisement)
assert device.last_service_info == advertisement
def test_encryption_needs_v2():
"""Test that we can detect what kind of encryption key a device needs."""
data_string = b"X0\xb6\x03\xd2\x8b\x98\xc5A$\xf8\xc3I\x14vu~\x00\x00\x00\x99"
advertisement = bytes_to_service_info(data_string, address="F8:24:41:C5:98:8B")
device = XiaomiBluetoothDeviceData()
assert device.supported(advertisement)
assert device.encryption_scheme == EncryptionScheme.MIBEACON_LEGACY
assert not device.bindkey_verified
def test_encryption_needs_v5():
"""Test that we can detect what kind of encryption key a device needs."""
data_string = b"XXH\x0bh_\x124-XZ\x0b\x18A\xe2\xaa\x00\x0e\x00\xa4\x96O\xb5"
advertisement = bytes_to_service_info(data_string, address="5A:58:2D:34:12:5F")
device = XiaomiBluetoothDeviceData()
assert device.supported(advertisement)
assert device.encryption_scheme == EncryptionScheme.MIBEACON_4_5
assert not device.bindkey_verified
def test_bindkey_wrong():
"""Test Xiaomi parser for RTCGQ02LM with wrong encryption key."""
bindkey = "814aac74c4f17b6c1581e1ab87816b99"
data_string = (
b"XY\x8d\n\x17\x0f\xc4\xe0D\xefT|" b"\xc2z\\\x03\xa1\x00\x00\x00y\r\xf2X"
)
advertisement = bytes_to_service_info(data_string, address="54:EF:44:E0:C4:0F")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert not device.bindkey_verified
assert device.decryption_failed
assert device.update(advertisement) == SensorUpdate(
title="Motion Sensor C40F (RTCGQ02LM)",
devices={
None: SensorDeviceInfo(
name="Motion Sensor C40F",
manufacturer="Xiaomi",
model="RTCGQ02LM",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
def test_bindkey_verified_can_be_unset_v4():
"""Test Xiaomi parser for RTCGQ02LM with wrong encryption key."""
bindkey = "814aac74c4f17b6c1581e1ab87816b99"
data_string = (
b"XY\x8d\n\x17\x0f\xc4\xe0D\xefT|" b"\xc2z\\\x03\xa1\x00\x00\x00y\r\xf2X"
)
advertisement = bytes_to_service_info(data_string, address="54:EF:44:E0:C4:0F")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
device.bindkey_verified = True
device.decryption_failed = False
assert device.supported(advertisement)
# the first advertisement will fail decryption, but we don't ask to reauth yet
assert device.bindkey_verified
assert device.decryption_failed
data_string = b"XY\x8d\n\x18\x0f\xc4\xe0D\xefT|\xc2z\\\x03\xa1\x00\x00\x00y"
advertisement = bytes_to_service_info(data_string, address="54:EF:44:E0:C4:0F")
assert device.supported(advertisement)
# the second advertisement will fail decryption again, but now we ask to reauth
assert device.decryption_failed
assert not device.bindkey_verified
def test_bindkey_wrong_legacy():
"""Test Xiaomi parser for YLKG07YL with wrong encryption key."""
bindkey = "b853075158487aa39a5b5ea9"
data_string = b"X0\xb6\x03\xd2\x8b\x98\xc5A$\xf8\xc3I\x14vu~\x00\x00\x00\x99"
advertisement = bytes_to_service_info(data_string, address="F8:24:41:C5:98:8B")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert not device.bindkey_verified
assert device.decryption_failed
assert device.update(advertisement) == SensorUpdate(
title="Dimmer Switch 988B (YLKG07YL/YLKG08YL)",
devices={
None: SensorDeviceInfo(
name="Dimmer Switch 988B",
manufacturer="Xiaomi",
model="YLKG07YL/YLKG08YL",
hw_version=None,
sw_version="Xiaomi (MiBeacon V3 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
assert device.unhandled == {}
def test_bindkey_verified_can_be_unset_legacy():
"""Test Xiaomi parser for YLKG07YL with wrong encryption key."""
bindkey = "b853075158487aa39a5b5ea9"
data_string = b"X0\xb6\x03\xd2\x8b\x98\xc5A$\xf8\xc3I\x14vu~\x00\x00\x00\x99"
advertisement = bytes_to_service_info(data_string, address="F8:24:41:C5:98:8B")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
device.bindkey_verified = True
device.decryption_failed = False
assert device.supported(advertisement)
# the first advertisement will fail decryption, but we don't ask to reauth yet
assert device.bindkey_verified
assert device.decryption_failed
data_string = b"X0\xb6\x03\xd3\x8b\x98\xc5A$\xf8\xc3I\x14vu~\x00\x00\x00\x99"
advertisement = bytes_to_service_info(data_string, address="F8:24:41:C5:98:8B")
assert device.supported(advertisement)
# the second advertisement will fail decryption again, but now we ask to reauth
assert device.decryption_failed
assert not device.bindkey_verified
def test_Xiaomi_unknown_device_logs_product_id(caplog):
"""An UNKNOWN Xiaomi device logs its decoded product_id and a readable MAC.
This is the single datum maintainers need to add support for a new device,
so it must appear in the INFO log rather than only being buried in the raw
advertisement bytes.
"""
# A valid MiBeacon V2 frame (the LYWSDCGQ advert) with the product_id bytes
# (data[2:4], little-endian) replaced by 0xFFFF, which is not registered.
data_string = b"P \xff\xff\xa3\xbf.;4-X\r\x10\x04\xb4\x00\x95\x02\n\x10\x01;"
advertisement = bytes_to_service_info(data_string, address="58:2D:34:3B:2E:BF")
device = XiaomiBluetoothDeviceData()
with caplog.at_level(logging.INFO):
assert not device.supported(advertisement)
assert "product_id: 0xffff" in caplog.text
# MAC is rendered human-readable, not as a raw bytes repr.
assert "58:2D:34:3B:2E:BF" in caplog.text
def test_Xiaomi_LYWSDCGQ(caplog):
"""Test Xiaomi parser for LYWSDCGQ."""
data_string = b"P \xaa\x01\xa3\xbf.;4-X\r\x10\x04\xb4\x00\x95\x02\n\x10\x01;"
advertisement = bytes_to_service_info(data_string, address="58:2D:34:3B:2E:BF")
device = XiaomiBluetoothDeviceData()
assert device.supported(advertisement)
assert not device.bindkey_verified
assert not device.sleepy_device
assert device.update(advertisement) == SensorUpdate(
title="Temperature/Humidity Sensor 2EBF (LYWSDCGQ)",
devices={
None: SensorDeviceInfo(
name="Temperature/Humidity Sensor 2EBF",
manufacturer="Xiaomi",
model="LYWSDCGQ",
sw_version="Xiaomi (MiBeacon V2)",
hw_version=None,
)
},
entity_descriptions={
KEY_TEMPERATURE: SensorDescription(
device_key=KEY_TEMPERATURE,
device_class=DeviceClass.TEMPERATURE,
native_unit_of_measurement="°C",
),
KEY_HUMIDITY: SensorDescription(
device_key=KEY_HUMIDITY,
device_class=DeviceClass.HUMIDITY,
native_unit_of_measurement="%",
),
KEY_VOLTAGE: SensorDescription(
device_key=KEY_VOLTAGE,
device_class=DeviceClass.VOLTAGE,
native_unit_of_measurement="V",
),
KEY_BATTERY: SensorDescription(
device_key=KEY_BATTERY,
device_class=DeviceClass.BATTERY,
native_unit_of_measurement="%",
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_TEMPERATURE: SensorValue(
device_key=KEY_TEMPERATURE, name="Temperature", native_value=18.0
),
KEY_HUMIDITY: SensorValue(
device_key=KEY_HUMIDITY, name="Humidity", native_value=66.1
),
KEY_VOLTAGE: SensorValue(
device_key=KEY_VOLTAGE, name="Voltage", native_value=1.272
),
KEY_BATTERY: SensorValue(
device_key=KEY_BATTERY, name="Battery", native_value=59.0
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
def test_Xiaomi_CGG1():
"""Test Xiaomi parser for CGG1."""
bindkey = "814aac74c4f17b6c1581e1ab87816b99"
data_string = b"XXH\x0bh_\x124-XZ\x0b\x18A\xe2\xaa\x00\x0e\x00\xa4\x96O\xb5"
advertisement = bytes_to_service_info(data_string, address="5A:58:2D:34:12:5F")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Temperature/Humidity Sensor 125F (CGG1-ENCRYPTED)",
devices={
None: SensorDeviceInfo(
name="Temperature/Humidity Sensor 125F",
manufacturer="Xiaomi",
model="CGG1-ENCRYPTED",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_HUMIDITY: SensorDescription(
device_key=KEY_HUMIDITY,
device_class=DeviceClass.HUMIDITY,
native_unit_of_measurement="%",
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_HUMIDITY: SensorValue(
name="Humidity", device_key=KEY_HUMIDITY, native_value=59.6
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
def test_Xiaomi_CGDK2():
"""Test Xiaomi parser for CGDK2."""
data_string = b"XXo\x06\x07\x89 \x124-X_\x17m\xd5O\x02\x00\x00/\xa4S\xfa"
bindkey = "a3bfe9853dd85a620debe3620caaa351"
advertisement = bytes_to_service_info(data_string, address="58:2D:34:12:20:89")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Temperature/Humidity Sensor 2089 (CGDK2)",
devices={
None: SensorDeviceInfo(
name="Temperature/Humidity Sensor 2089",
manufacturer="Xiaomi",
model="CGDK2",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_TEMPERATURE: SensorDescription(
device_key=KEY_TEMPERATURE,
device_class=DeviceClass.TEMPERATURE,
native_unit_of_measurement="°C",
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_TEMPERATURE: SensorValue(
name="Temperature", device_key=KEY_TEMPERATURE, native_value=22.6
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
def test_Xiaomi_CGDK3():
"""Test Xiaomi parser for CGDK3."""
data_string = b"HXYO\xefTo\x0etPr\x93!\x01\x008\xd7C3"
bindkey = "872ea1bb02ba9713e885cb054c537368"
advertisement = bytes_to_service_info(data_string, address="58:2D:34:87:94:0C")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Temperature/Humidity Sensor 940C (CGDK3)",
devices={
None: SensorDeviceInfo(
name="Temperature/Humidity Sensor 940C",
manufacturer="Xiaomi",
model="CGDK3",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_TEMPERATURE: SensorDescription(
device_key=KEY_TEMPERATURE,
device_class=DeviceClass.TEMPERATURE,
native_unit_of_measurement="°C",
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_TEMPERATURE: SensorValue(
name="Temperature", device_key=KEY_TEMPERATURE, native_value=23.8
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
def test_Xiaomi_LYWSD02():
"""Test Xiaomi parser for LYWSD02."""
def test_Xiaomi_LYWSD03MMC():
"""Test Xiaomi parser for LYWSD03MMC without encryption."""
data_string = b"P0[\x05\x03L\x94\xb48\xc1\xa4\r\x10\x04\x10\x01\xea\x01"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:B4:94:4C")
device = XiaomiBluetoothDeviceData()
assert device.supported(advertisement)
assert not device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Temperature/Humidity Sensor 944C (LYWSD03MMC)",
devices={
None: SensorDeviceInfo(
name="Temperature/Humidity Sensor 944C",
manufacturer="Xiaomi",
model="LYWSD03MMC",
hw_version=None,
sw_version="Xiaomi (MiBeacon V3)",
)
},
entity_descriptions={
KEY_TEMPERATURE: SensorDescription(
device_key=KEY_TEMPERATURE,
device_class=DeviceClass.TEMPERATURE,
native_unit_of_measurement="°C",
),
KEY_HUMIDITY: SensorDescription(
device_key=KEY_HUMIDITY,
device_class=DeviceClass.HUMIDITY,
native_unit_of_measurement="%",
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_TEMPERATURE: SensorValue(
name="Temperature", device_key=KEY_TEMPERATURE, native_value=27.2
),
KEY_HUMIDITY: SensorValue(
name="Humidity", device_key=KEY_HUMIDITY, native_value=49.0
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
def test_Xiaomi_LYWSD02MMC():
"""Test Xiaomi parser for LYWSD02MMC."""
bindkey = "a115210eed7a88e50ad52662e732a9fb"
data_string = b"XX\xe4\x16,\x84SV8\xc1\xa4+n\xf2\xe9\x12\x00\x00l\x88M\x9e"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:56:53:84")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Temperature/Humidity Sensor 5384 (LYWSD02MMC)",
devices={
None: SensorDeviceInfo(
name="Temperature/Humidity Sensor 5384",
manufacturer="Xiaomi",
model="LYWSD02MMC",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_HUMIDITY: SensorDescription(
device_key=KEY_HUMIDITY,
device_class=DeviceClass.HUMIDITY,
native_unit_of_measurement="%",
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_HUMIDITY: SensorValue(
name="Humidity", device_key=KEY_HUMIDITY, native_value=58
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
def test_Xiaomi_LYWSD02MMC_v2_temperature():
"""Test Xiaomi parser for LYWSD02MMC updated version temperature."""
bindkey = "19b1c678ab0a8bc3dc77765f059188d4"
data_string = b'HXB%) -\x8czv\xb7V\xa8*\x00x\xb8"N'
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:0E:FD:78")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Temperature/Humidity Sensor FD78 (LYWSD02MMC)",
devices={
None: SensorDeviceInfo(
name="Temperature/Humidity Sensor FD78",
manufacturer="Xiaomi",
model="LYWSD02MMC",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_TEMPERATURE: SensorDescription(
device_key=KEY_TEMPERATURE,
device_class=DeviceClass.TEMPERATURE,
native_unit_of_measurement="°C",
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_TEMPERATURE: SensorValue(
name="Temperature", device_key=KEY_TEMPERATURE, native_value=26.10
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
def test_Xiaomi_LYWSD02MMC_v2_humidity():
"""Test Xiaomi parser for LYWSD02MMC updated version humidity."""
bindkey = "19b1c678ab0a8bc3dc77765f059188d4"
data_string = b"XXB%\x88x\xfd\x0e8\xc1\xa4\x05\xf6S\x8a\xa7*\x00b\xb1\xa9f"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:0E:FD:78")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Temperature/Humidity Sensor FD78 (LYWSD02MMC)",
devices={
None: SensorDeviceInfo(
name="Temperature/Humidity Sensor FD78",
manufacturer="Xiaomi",
model="LYWSD02MMC",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_HUMIDITY: SensorDescription(
device_key=KEY_HUMIDITY,
device_class=DeviceClass.HUMIDITY,
native_unit_of_measurement="%",
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_HUMIDITY: SensorValue(
name="Humidity", device_key=KEY_HUMIDITY, native_value=38
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
def test_Xiaomi_LYWSD03MMC_encrypted():
"""Test Xiaomi parser for LYWSD03MMC with encryption."""
data_string = (
b"XX[\x05P\xf4\x83\x028" b"\xc1\xa4\x95\xefXv<&\x00\x00\x97\xe2\xab\xb5"
)
bindkey = "e9ea895fac7cca6d30532432a516f3a8"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:02:83:F4")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Temperature/Humidity Sensor 83F4 (LYWSD03MMC)",
devices={
None: SensorDeviceInfo(
name="Temperature/Humidity Sensor 83F4",
manufacturer="Xiaomi",
model="LYWSD03MMC",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_HUMIDITY: SensorDescription(
device_key=KEY_HUMIDITY,
device_class=DeviceClass.HUMIDITY,
native_unit_of_measurement="%",
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_HUMIDITY: SensorValue(
name="Humidity", device_key=KEY_HUMIDITY, native_value=46
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
def test_Xiaomi_XMWSDJ04MMC():
"""Test Xiaomi parser for XMWSDJ04MMC with encryption."""
bindkey = "b2cf9a553d53571b5657defd582d676e"
data_string = b"HY\x03\x12\xa4\x1bwn|\x96\xad\xd7\x00\x00\x00\xf2\xbfT["
advertisement = bytes_to_service_info(data_string, address="2C:11:65:25:70:04")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Thermometer 7004 (XMWSDJ04MMC)",
devices={
None: SensorDeviceInfo(
name="Thermometer 7004",
manufacturer="Xiaomi",
model="XMWSDJ04MMC",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_HUMIDITY: SensorDescription(
device_key=KEY_HUMIDITY,
device_class=DeviceClass.HUMIDITY,
native_unit_of_measurement="%",
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_HUMIDITY: SensorValue(
name="Humidity", device_key=KEY_HUMIDITY, native_value=45.0
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
def test_Xiaomi_XMMF01JQD():
"""Test Xiaomi parser for XMMF01JQD."""
data_string = b"P0\xe1\x04\x8eT\xd3\xe60S\xe2\x01\x10\x03\x01\x00\x00"
advertisement = bytes_to_service_info(data_string, address="E2:53:30:E6:D3:54")
device = XiaomiBluetoothDeviceData()
assert device.supported(advertisement)
assert not device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Magic Cube D354 (XMMF01JQD)",
devices={
None: SensorDeviceInfo(
name="Magic Cube D354",
manufacturer="Xiaomi",
model="XMMF01JQD",
hw_version=None,
sw_version="Xiaomi (MiBeacon V3)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
events={
KEY_EVENT_CUBE: Event(
device_key=KEY_EVENT_CUBE,
name="Cube",
event_type="rotate_left",
event_properties=None,
),
},
)
def test_Xiaomi_CGC1():
"""Test Xiaomi parser for CGC1."""
def test_Xiaomi_CGD1():
"""Test Xiaomi parser for CGD1."""
def test_Xiaomi_CGP1W():
"""Test Xiaomi parser for CGP1W."""
def test_Xiaomi_MHO_C303():
"""Test Xiaomi parser for MHO-C303."""
def test_Xiaomi_MHO_C401():
"""Test Xiaomi parser for MHO-C401."""
def test_Xiaomi_JQJCY01YM1():
"""Test Xiaomi parser for JQJCY01YM."""
def test_Xiaomi_JTYJGD03MI_smoke():
"""Test Xiaomi parser for JTYJGD03MI."""
bindkey = "5b51a7c91cde6707c9ef18dfda143a58"
data_string = (
b"XY\x97\tf\xbc\x9c\xe3D\xefT\x01" b"\x08\x12\x05\x00\x00\x00q^\xbe\x90"
)
advertisement = bytes_to_service_info(data_string, address="54:EF:44:E3:9C:BC")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Smoke Detector 9CBC (JTYJGD03MI)",
devices={
None: SensorDeviceInfo(
name="Smoke Detector 9CBC",
manufacturer="Xiaomi",
model="JTYJGD03MI",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_SMOKE: BinarySensorDescription(
device_key=KEY_SMOKE,
device_class=BinarySensorDeviceClass.SMOKE,
),
},
binary_entity_values={
KEY_SMOKE: BinarySensorValue(
name="Smoke", device_key=KEY_SMOKE, native_value=True
),
},
)
def test_Xiaomi_JTYJGD03MI_press():
"""Test Xiaomi parser for JTYJGD03MI."""
bindkey = "5b51a7c91cde6707c9ef18dfda143a58"
data_string = (
b'XY\x97\td\xbc\x9c\xe3D\xefT" `' b"\x88\xfd\x00\x00\x00\x00:\x14\x8f\xb3"
)
advertisement = bytes_to_service_info(data_string, address="54:EF:44:E3:9C:BC")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Smoke Detector 9CBC (JTYJGD03MI)",
devices={
None: SensorDeviceInfo(
name="Smoke Detector 9CBC",
manufacturer="Xiaomi",
model="JTYJGD03MI",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
events={
KEY_EVENT_BUTTON: Event(
device_key=KEY_EVENT_BUTTON,
name="Button",
event_type="press",
event_properties=None,
),
},
)
def test_Xiaomi_HHCCJCY01():
"""Test Xiaomi parser for HHCCJCY01."""
data_string = b"q \x98\x00\x12\xf3Ok\x8d|\xc4\r\x04\x10\x02\xc4\x00"
advertisement = bytes_to_service_info(data_string, address="C4:7C:8D:6B:4F:F3")
device = XiaomiBluetoothDeviceData()
assert device.supported(advertisement)
assert not device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Plant Sensor 4FF3 (HHCCJCY01)",
devices={
None: SensorDeviceInfo(
name="Plant Sensor 4FF3",
manufacturer="Xiaomi",
model="HHCCJCY01",
hw_version=None,
sw_version="Xiaomi (MiBeacon V2)",
)
},
entity_descriptions={
KEY_TEMPERATURE: SensorDescription(
device_key=KEY_TEMPERATURE,
device_class=DeviceClass.TEMPERATURE,
native_unit_of_measurement="°C",
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_TEMPERATURE: SensorValue(
name="Temperature", device_key=KEY_TEMPERATURE, native_value=19.6
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
def test_Xiaomi_HHCCJCY01_all_values():
"""Test Xiaomi parser for HHCCJCY01."""
device = XiaomiBluetoothDeviceData()
device.update(
bytes_to_service_info(
b"q \x98\x00fz>j\x8d|\xc4\r\x07\x10\x03\x00\x00\x00",
address="C4:7C:8D:6A:3E:7A",
)
)
device.update(
bytes_to_service_info(
b"q \x98\x00hz>j\x8d|\xc4\r\t\x10\x02W\x02", address="C4:7C:8D:6A:3E:7A"
)
)
device.update(
bytes_to_service_info(
b"q \x98\x00Gz>j\x8d|\xc4\r\x08\x10\x01@", address="C4:7C:8D:6A:3E:7A"
)
)
assert device.update(
bytes_to_service_info(
b"q \x98\x00iz>j\x8d|\xc4\r\x04\x10\x02\xf4\x00",
address="C4:7C:8D:6A:3E:7A",
)
) == SensorUpdate(
title="Plant Sensor 3E7A (HHCCJCY01)",
devices={
None: SensorDeviceInfo(
name="Plant Sensor 3E7A",
manufacturer="Xiaomi",
model="HHCCJCY01",
hw_version=None,
sw_version="Xiaomi (MiBeacon V2)",
)
},
entity_descriptions={
KEY_TEMPERATURE: SensorDescription(
device_key=KEY_TEMPERATURE,
device_class=DeviceClass.TEMPERATURE,
native_unit_of_measurement=Units.TEMP_CELSIUS,
),
KEY_ILLUMINANCE: SensorDescription(
device_key=KEY_ILLUMINANCE,
device_class=DeviceClass.ILLUMINANCE,
native_unit_of_measurement=Units.LIGHT_LUX,
),
KEY_CONDUCTIVITY: SensorDescription(
device_key=KEY_CONDUCTIVITY,
device_class=DeviceClass.CONDUCTIVITY,
native_unit_of_measurement=Units.CONDUCTIVITY,
),
KEY_MOISTURE: SensorDescription(
device_key=KEY_MOISTURE,
device_class=DeviceClass.MOISTURE,
native_unit_of_measurement=Units.PERCENTAGE,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
),
},
entity_values={
KEY_TEMPERATURE: SensorValue(
name="Temperature", device_key=KEY_TEMPERATURE, native_value=24.4
),
KEY_ILLUMINANCE: SensorValue(
name="Illuminance", device_key=KEY_ILLUMINANCE, native_value=0
),
KEY_CONDUCTIVITY: SensorValue(
name="Conductivity", device_key=KEY_CONDUCTIVITY, native_value=599
),
KEY_MOISTURE: SensorValue(
name="Moisture", device_key=KEY_MOISTURE, native_value=64
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
def test_Xiaomi_HHCCJCY10():
"""Test Xiaomi parser for HHCCJCY10."""
device = XiaomiBluetoothDeviceData()
assert device.update(
BluetoothServiceInfo(
name="Test",
address="DC:23:4D:E5:5B:FC",
rssi=-60,
manufacturer_data={},
service_data={SERVICE_HHCCJCY10: b"\x0e\x00n\x014\xa4(\x00["},
service_uuids=[SERVICE_HHCCJCY10],
source="",
)
) == SensorUpdate(
title="Plant Sensor 5BFC (HHCCJCY10)",
devices={
None: SensorDeviceInfo(
name="Plant Sensor 5BFC",
manufacturer="HHCC Plant Technology Co. Ltd",
model="HHCCJCY10",
hw_version=None,
sw_version=None,
)
},
entity_descriptions={
KEY_TEMPERATURE: SensorDescription(
device_key=KEY_TEMPERATURE,
device_class=DeviceClass.TEMPERATURE,
native_unit_of_measurement=Units.TEMP_CELSIUS,
),
KEY_ILLUMINANCE: SensorDescription(
device_key=KEY_ILLUMINANCE,
device_class=DeviceClass.ILLUMINANCE,
native_unit_of_measurement=Units.LIGHT_LUX,
),
KEY_CONDUCTIVITY: SensorDescription(
device_key=KEY_CONDUCTIVITY,
device_class=DeviceClass.CONDUCTIVITY,
native_unit_of_measurement=Units.CONDUCTIVITY,
),
KEY_MOISTURE: SensorDescription(
device_key=KEY_MOISTURE,
device_class=DeviceClass.MOISTURE,
native_unit_of_measurement=Units.PERCENTAGE,
),
KEY_BATTERY: SensorDescription(
device_key=KEY_BATTERY,
device_class=DeviceClass.BATTERY,
native_unit_of_measurement=Units.PERCENTAGE,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
),
},
entity_values={
KEY_TEMPERATURE: SensorValue(
name="Temperature", device_key=KEY_TEMPERATURE, native_value=11.0
),
KEY_ILLUMINANCE: SensorValue(
name="Illuminance", device_key=KEY_ILLUMINANCE, native_value=79012
),
KEY_CONDUCTIVITY: SensorValue(
name="Conductivity", device_key=KEY_CONDUCTIVITY, native_value=91
),
KEY_MOISTURE: SensorValue(
name="Moisture", device_key=KEY_MOISTURE, native_value=14
),
KEY_BATTERY: SensorValue(
name="Battery", device_key=KEY_BATTERY, native_value=40
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
def test_Xiaomi_Scale1():
"""Test Xiaomi parser for Mi Smart Scale (MiScale V1)"""
data_string = b"\x22\x9e\x43\xe5\x07\x04\x0b\x10\x13\x01"
device = XiaomiBluetoothDeviceData()
assert device.update(
BluetoothServiceInfo(
name="MISCA",
address="50:FB:19:1B:B5:DC",
rssi=-60,
manufacturer_data={},
service_data={SERVICE_SCALE1: data_string},
service_uuids=[SERVICE_SCALE1],
source="",
)
) == SensorUpdate(
title="Mi Smart Scale (B5DC)",
devices={
None: SensorDeviceInfo(
name="Mi Smart Scale (B5DC)",
manufacturer="Xiaomi",
model="XMTZC01HM/XMTZC04HM",
hw_version=None,
sw_version=None,
)
},
entity_descriptions={
KEY_MASS_NON_STABILIZED: SensorDescription(
device_key=KEY_MASS_NON_STABILIZED,
device_class=DeviceClass.MASS_NON_STABILIZED,
native_unit_of_measurement=Units.MASS_KILOGRAMS,
),
KEY_MASS: SensorDescription(
device_key=KEY_MASS,
device_class=DeviceClass.MASS,
native_unit_of_measurement=Units.MASS_KILOGRAMS,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
),
},
entity_values={
KEY_MASS_NON_STABILIZED: SensorValue(
name="Mass Non Stabilized",
device_key=KEY_MASS_NON_STABILIZED,
native_value=86.55,
),
KEY_MASS: SensorValue(
name="Mass",
device_key=KEY_MASS,
native_value=86.55,
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_STABILIZED: BinarySensorDescription(
device_key=KEY_STABILIZED,
device_class=ExtendedBinarySensorDeviceClass.STABILIZED,
),
},
binary_entity_values={
KEY_STABILIZED: BinarySensorValue(
name="Stabilized", device_key=KEY_STABILIZED, native_value=True
),
},
)
def test_Xiaomi_Scale1_mass_removed():
"""Test Xiaomi parser for Mi Smart Scale (MiScale V1) mass removed"""
data_string = b"\xa2 D\xb2\x07\x01\x01\n\x1a\x15"
device = XiaomiBluetoothDeviceData()
assert device.update(
BluetoothServiceInfo(
name="MISCA",
address="50:FB:19:1B:B5:DC",
rssi=-60,
manufacturer_data={},
service_data={SERVICE_SCALE1: data_string},
service_uuids=[SERVICE_SCALE1],
source="",
)
) == SensorUpdate(
title="Mi Smart Scale (B5DC)",
devices={
None: SensorDeviceInfo(
name="Mi Smart Scale (B5DC)",
manufacturer="Xiaomi",
model="XMTZC01HM/XMTZC04HM",
hw_version=None,
sw_version=None,
)
},
entity_descriptions={
KEY_MASS_NON_STABILIZED: SensorDescription(
device_key=KEY_MASS_NON_STABILIZED,
device_class=DeviceClass.MASS_NON_STABILIZED,
native_unit_of_measurement=Units.MASS_KILOGRAMS,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
),
},
entity_values={
KEY_MASS_NON_STABILIZED: SensorValue(
name="Mass Non Stabilized",
device_key=KEY_MASS_NON_STABILIZED,
native_value=87.2,
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_STABILIZED: BinarySensorDescription(
device_key=KEY_STABILIZED,
device_class=ExtendedBinarySensorDeviceClass.STABILIZED,
),
},
binary_entity_values={
KEY_STABILIZED: BinarySensorValue(
name="Stabilized", device_key=KEY_STABILIZED, native_value=False
),
},
)
def test_Xiaomi_Scale1_non_stabilized():
"""Test Xiaomi parser for Mi Smart Scale (MiScale V1) non stabilized"""
data_string = b"\x82\x14\x00\xe5\x07\x04\x0b\x10\x17\x08"
device = XiaomiBluetoothDeviceData()
assert device.update(
BluetoothServiceInfo(
name="MISCA",
address="50:FB:19:1B:B5:DC",
rssi=-60,
manufacturer_data={},
service_data={SERVICE_SCALE1: data_string},
service_uuids=[SERVICE_SCALE1],
source="",
)
) == SensorUpdate(
title="Mi Smart Scale (B5DC)",
devices={
None: SensorDeviceInfo(
name="Mi Smart Scale (B5DC)",
manufacturer="Xiaomi",
model="XMTZC01HM/XMTZC04HM",
hw_version=None,
sw_version=None,
)
},
entity_descriptions={
KEY_MASS_NON_STABILIZED: SensorDescription(
device_key=KEY_MASS_NON_STABILIZED,
device_class=DeviceClass.MASS_NON_STABILIZED,
native_unit_of_measurement=Units.MASS_KILOGRAMS,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
),
},
entity_values={
KEY_MASS_NON_STABILIZED: SensorValue(
name="Mass Non Stabilized",
device_key=KEY_MASS_NON_STABILIZED,
native_value=0.1,
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_STABILIZED: BinarySensorDescription(
device_key=KEY_STABILIZED,
device_class=ExtendedBinarySensorDeviceClass.STABILIZED,
),
},
binary_entity_values={
KEY_STABILIZED: BinarySensorValue(
name="Stabilized", device_key=KEY_STABILIZED, native_value=False
),
},
)
def test_Xiaomi_Scale2():
"""Test Xiaomi parser for Mi Body Composition Scale (MiScale V2)"""
data_string = b"\x02&\xb2\x07\x05\x04\x0f\x02\x01\xac\x01\x86B"
device = XiaomiBluetoothDeviceData()
assert device.update(
BluetoothServiceInfo(
name="MIBFS",
address="50:FB:19:1B:B5:DC",
rssi=-60,
manufacturer_data={},
service_data={SERVICE_SCALE2: data_string},
service_uuids=[SERVICE_SCALE2],
source="",
)
) == SensorUpdate(
title="Mi Body Composition Scale (B5DC)",
devices={
None: SensorDeviceInfo(
name="Mi Body Composition Scale (B5DC)",
manufacturer="Xiaomi",
model="XMTZC02HM/XMTZC05HM/NUN4049CN",
hw_version=None,
sw_version=None,
)
},
entity_descriptions={
KEY_MASS_NON_STABILIZED: SensorDescription(
device_key=KEY_MASS_NON_STABILIZED,
device_class=DeviceClass.MASS_NON_STABILIZED,
native_unit_of_measurement=Units.MASS_KILOGRAMS,
),
KEY_MASS: SensorDescription(
device_key=KEY_MASS,
device_class=DeviceClass.MASS,
native_unit_of_measurement=Units.MASS_KILOGRAMS,
),
KEY_IMPEDANCE: SensorDescription(
device_key=KEY_IMPEDANCE,
device_class=DeviceClass.IMPEDANCE,
native_unit_of_measurement=Units.OHM,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
),
},
entity_values={
KEY_MASS_NON_STABILIZED: SensorValue(
name="Mass Non Stabilized",
device_key=KEY_MASS_NON_STABILIZED,
native_value=85.15,
),
KEY_MASS: SensorValue(
name="Mass",
device_key=KEY_MASS,
native_value=85.15,
),
KEY_IMPEDANCE: SensorValue(
name="Impedance", device_key=KEY_IMPEDANCE, native_value=428
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_STABILIZED: BinarySensorDescription(
device_key=KEY_STABILIZED,
device_class=ExtendedBinarySensorDeviceClass.STABILIZED,
),
},
binary_entity_values={
KEY_STABILIZED: BinarySensorValue(
name="Stabilized", device_key=KEY_STABILIZED, native_value=True
),
},
)
def test_Xiaomi_Scale2_non_stabilized():
"""Test Xiaomi parser for Mi Body Composition Scale (MiScale v2) (non stabilized)"""
data_string = b"\x02\x04\xb2\x07\x01\x01\x12\x10\x1a\x00\x00\xa8R"
device = XiaomiBluetoothDeviceData()
assert device.update(
BluetoothServiceInfo(
name="MIBFS",
address="50:FB:19:1B:B5:DC",
rssi=-60,
manufacturer_data={},
service_data={SERVICE_SCALE2: data_string},
service_uuids=[SERVICE_SCALE2],
source="",
)
) == SensorUpdate(
title="Mi Body Composition Scale (B5DC)",
devices={
None: SensorDeviceInfo(
name="Mi Body Composition Scale (B5DC)",
manufacturer="Xiaomi",
model="XMTZC02HM/XMTZC05HM/NUN4049CN",
hw_version=None,
sw_version=None,
)
},
entity_descriptions={
KEY_MASS_NON_STABILIZED: SensorDescription(
device_key=KEY_MASS_NON_STABILIZED,
device_class=DeviceClass.MASS_NON_STABILIZED,
native_unit_of_measurement=Units.MASS_KILOGRAMS,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
),
},
entity_values={
KEY_MASS_NON_STABILIZED: SensorValue(
name="Mass Non Stabilized",
device_key=KEY_MASS_NON_STABILIZED,
native_value=105.8,
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_STABILIZED: BinarySensorDescription(
device_key=KEY_STABILIZED,
device_class=ExtendedBinarySensorDeviceClass.STABILIZED,
),
},
binary_entity_values={
KEY_STABILIZED: BinarySensorValue(
name="Stabilized", device_key=KEY_STABILIZED, native_value=False
),
},
)
def test_Xiaomi_Scale1_reset():
"""Test Xiaomi parser for Mi Smart Scale (MiScale V1) — person stepped off scale."""
# control_byte=0x80 (mass_removed=True), mass=0
data_string = b"\x80\x00\x00\xe5\x07\x04\x0b\x10\x13\x01"
device = XiaomiBluetoothDeviceData()
assert device.update(
BluetoothServiceInfo(
name="MISCA",
address="50:FB:19:1B:B5:DC",
rssi=-60,
manufacturer_data={},
service_data={SERVICE_SCALE1: data_string},
service_uuids=[SERVICE_SCALE1],
source="",
)
) == SensorUpdate(
title="Mi Smart Scale (B5DC)",
devices={
None: SensorDeviceInfo(
name="Mi Smart Scale (B5DC)",
manufacturer="Xiaomi",
model="XMTZC01HM/XMTZC04HM",
hw_version=None,
sw_version=None,
)
},
entity_descriptions={
KEY_MASS_NON_STABILIZED: SensorDescription(
device_key=KEY_MASS_NON_STABILIZED,
device_class=DeviceClass.MASS_NON_STABILIZED,
native_unit_of_measurement=Units.MASS_KILOGRAMS,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
),
},
entity_values={
KEY_MASS_NON_STABILIZED: SensorValue(
name="Mass Non Stabilized",
device_key=KEY_MASS_NON_STABILIZED,
native_value=0.0,
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_STABILIZED: BinarySensorDescription(
device_key=KEY_STABILIZED,
device_class=ExtendedBinarySensorDeviceClass.STABILIZED,
),
},
binary_entity_values={
KEY_STABILIZED: BinarySensorValue(
name="Stabilized", device_key=KEY_STABILIZED, native_value=False
),
},
)
def test_Xiaomi_Scale2_reset():
"""Test Xiaomi parser for Mi Body Composition Scale (MiScale V2).
Person stepped off scale.
"""
# control_bytes: mass_removed=True, mass_stabilized=False, mass=0
data_string = b"\x02\x82\xb2\x07\x05\x04\x0f\x02\x01\x00\x00\x00\x00"
device = XiaomiBluetoothDeviceData()
assert device.update(
BluetoothServiceInfo(
name="MIBFS",
address="50:FB:19:1B:B5:DC",
rssi=-60,
manufacturer_data={},
service_data={SERVICE_SCALE2: data_string},
service_uuids=[SERVICE_SCALE2],
source="",
)
) == SensorUpdate(
title="Mi Body Composition Scale (B5DC)",
devices={
None: SensorDeviceInfo(
name="Mi Body Composition Scale (B5DC)",
manufacturer="Xiaomi",
model="XMTZC02HM/XMTZC05HM/NUN4049CN",
hw_version=None,
sw_version=None,
)
},
entity_descriptions={
KEY_MASS_NON_STABILIZED: SensorDescription(
device_key=KEY_MASS_NON_STABILIZED,
device_class=DeviceClass.MASS_NON_STABILIZED,
native_unit_of_measurement=Units.MASS_KILOGRAMS,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
),
},
entity_values={
KEY_MASS_NON_STABILIZED: SensorValue(
name="Mass Non Stabilized",
device_key=KEY_MASS_NON_STABILIZED,
native_value=0.0,
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_STABILIZED: BinarySensorDescription(
device_key=KEY_STABILIZED,
device_class=ExtendedBinarySensorDeviceClass.STABILIZED,
),
},
binary_entity_values={
KEY_STABILIZED: BinarySensorValue(
name="Stabilized", device_key=KEY_STABILIZED, native_value=False
),
},
)
def test_Xiaomi_GCLS002():
"""Test Xiaomi parser for GCLS002 / HHCCJCY09."""
data_string = b"q \xbc\x03\xcd>Ym\x8d|\xc4\r\x04\x10\x02<\x01"
advertisement = bytes_to_service_info(data_string, address="C4:7C:8D:6D:59:3E")
device = XiaomiBluetoothDeviceData()
assert device.supported(advertisement)
assert not device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Grow Care Garden 593E (GCLS002)",
devices={
None: SensorDeviceInfo(
name="Grow Care Garden 593E",
manufacturer="Xiaomi",
model="GCLS002",
hw_version=None,
sw_version="Xiaomi (MiBeacon V2)",
)
},
entity_descriptions={
KEY_TEMPERATURE: SensorDescription(
device_key=KEY_TEMPERATURE,
device_class=DeviceClass.TEMPERATURE,
native_unit_of_measurement="°C",
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_TEMPERATURE: SensorValue(
name="Temperature", device_key=KEY_TEMPERATURE, native_value=31.6
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
def test_Xiaomi_HHCCPOT002():
"""Test Xiaomi parser for HHCCPOT002."""
def test_Xiaomi_WX08ZM():
"""Test Xiaomi parser for WX08ZM."""
def test_Xiaomi_MCCGQ02HL():
"""Test Xiaomi parser for MCCGQ02HL."""
data_string = b"XX\x8b\t\xa3\xae!\x81\xec\xaa\xe4\x0e,U<\x04\x00\x00\xd2\x8aP\x0c"
advertisement = bytes_to_service_info(data_string, address="E4:AA:EC:81:21:AE")
bindkey = "017e52d2684779298709b117c0a75a7b"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Door/Window Sensor 21AE (MCCGQ02HL)",
devices={
None: SensorDeviceInfo(
name="Door/Window Sensor 21AE",
manufacturer="Xiaomi",
model="MCCGQ02HL",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_BINARY_OPENING: BinarySensorDescription(
device_key=KEY_BINARY_OPENING,
device_class=BinarySensorDeviceClass.OPENING,
),
KEY_BINARY_DOOR_LEFT_OPEN: BinarySensorDescription(
device_key=KEY_BINARY_DOOR_LEFT_OPEN,
device_class=ExtendedBinarySensorDeviceClass.DOOR_LEFT_OPEN,
),
},
binary_entity_values={
KEY_BINARY_OPENING: BinarySensorValue(
device_key=KEY_BINARY_OPENING, name="Opening", native_value=True
),
KEY_BINARY_DOOR_LEFT_OPEN: BinarySensorValue(
device_key=KEY_BINARY_DOOR_LEFT_OPEN,
name="Door left open",
native_value=True,
),
},
)
def test_Xiaomi_CGH1():
"""Test Xiaomi parser for CGH1."""
def test_Xiaomi_YM_K1501():
"""Test Xiaomi parser for YM-K1501."""
def test_Xiaomi_V_SK152():
"""Test Xiaomi parser for V-SK152."""
def test_Xiaomi_SJWS01LM():
"""Test Xiaomi parser for SJWS01LM."""
def test_Xiaomi_MJWSD06MMC_temperature():
"""Test Xiaomi parser for MJWSD06MMC without encryption."""
data_string = b"HY\xb5U:\x86\x99\xbd\xa0SD\x8f\x12\x00\x00[\x04mj"
bindkey = "4d8f1373fb4d3bab557d0ebd1c78f8c4"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:80:15:07")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Temperature/Humidity Sensor 1507 (MJWSD06MMC)",
devices={
None: SensorDeviceInfo(
name="Temperature/Humidity Sensor 1507",
manufacturer="Xiaomi",
model="MJWSD06MMC",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_TEMPERATURE: SensorDescription(
device_key=KEY_TEMPERATURE,
device_class=DeviceClass.TEMPERATURE,
native_unit_of_measurement="°C",
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_TEMPERATURE: SensorValue(
name="Temperature", device_key=KEY_TEMPERATURE, native_value=25.2
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
def test_Xiaomi_MJWSD06MMC_humidity():
"""Test Xiaomi parser for MJWSD06MMC with encryption."""
data_string = b"XY\xb5U4\x07\x15\x808\xc1\xa4\xbc\xc72\x98\x0e\x00\x00f\x96\x0f\x10"
bindkey = "4d8f1373fb4d3bab557d0ebd1c78f8c4"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:80:15:07")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Temperature/Humidity Sensor 1507 (MJWSD06MMC)",
devices={
None: SensorDeviceInfo(
name="Temperature/Humidity Sensor 1507",
manufacturer="Xiaomi",
model="MJWSD06MMC",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_HUMIDITY: SensorDescription(
device_key=KEY_HUMIDITY,
device_class=DeviceClass.HUMIDITY,
native_unit_of_measurement="%",
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_HUMIDITY: SensorValue(
name="Humidity", device_key=KEY_HUMIDITY, native_value=39
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
def test_Xiaomi_MJWSD06MMC_new_hw_revision():
"""Test MJWSD06MMC new hw revision (product_id 0x5BEA, July 2025 batch).
Same model as 0x55B5 but Xiaomi shipped units broadcasting product_id
0x5BEA, which left them undiscovered. See GH #277. The advert below is
the 0x55B5 temperature payload re-encrypted under the new product_id, so
it must decode identically.
"""
data_string = b"HY\xea[:\xe4\xda\x12\xe2\xb1\xfb\xf5\x12\x00\x00B\xb2\x9fP"
bindkey = "4d8f1373fb4d3bab557d0ebd1c78f8c4"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:80:15:07")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Temperature/Humidity Sensor 1507 (MJWSD06MMC)",
devices={
None: SensorDeviceInfo(
name="Temperature/Humidity Sensor 1507",
manufacturer="Xiaomi",
model="MJWSD06MMC",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_TEMPERATURE: SensorDescription(
device_key=KEY_TEMPERATURE,
device_class=DeviceClass.TEMPERATURE,
native_unit_of_measurement="°C",
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_TEMPERATURE: SensorValue(
name="Temperature", device_key=KEY_TEMPERATURE, native_value=25.2
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
def test_Xiaomi_ESM787_discovery():
"""Test Xiaomi parser for the Yanmi ESM787 (product_id 0x78DB).
The raw advertisement reported in GH #295 is an unencrypted MiBeacon V5
discovery beacon (object_include=0): it carries the MAC but no measurement
object. Registering 0x78DB is enough to identify the device so Home
Assistant can discover it; sensor values arrive on later object-bearing
adverts (see test_Xiaomi_ESM787_temperature_humidity).
"""
data_string = b"\x10Y\xdbx\r\xf4\xf8\x028\xc1\xa4"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:02:F8:F4")
device = XiaomiBluetoothDeviceData()
assert device.supported(advertisement)
assert device.update(advertisement) == SensorUpdate(
title="Temperature/Humidity Sensor F8F4 (ESM787)",
devices={
None: SensorDeviceInfo(
name="Temperature/Humidity Sensor F8F4",
manufacturer="Yanmi",
model="ESM787",
hw_version=None,
sw_version=None,
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
def test_Xiaomi_ESM787_temperature_humidity():
"""Test Xiaomi parser for the Yanmi ESM787 temperature + humidity.
Synthesized unencrypted MiBeacon V5 advert for product_id 0x78DB carrying a
standard combined temperature/humidity object (obj100d). Built from the real
MAC/product_id reported in GH #295 with the object_include frame-control bit
set, so it exercises the registered device through the established obj100d
decoder (temp=23.5 °C, humidity=48.7 %).
"""
data_string = b"PY\xdbx\r\xf4\xf8\x028\xc1\xa4\r\x10\x04\xeb\x00\xe7\x01"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:02:F8:F4")
device = XiaomiBluetoothDeviceData()
assert device.supported(advertisement)
assert device.update(advertisement) == SensorUpdate(
title="Temperature/Humidity Sensor F8F4 (ESM787)",
devices={
None: SensorDeviceInfo(
name="Temperature/Humidity Sensor F8F4",
manufacturer="Yanmi",
model="ESM787",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5)",
)
},
entity_descriptions={
KEY_TEMPERATURE: SensorDescription(
device_key=KEY_TEMPERATURE,
device_class=DeviceClass.TEMPERATURE,
native_unit_of_measurement="°C",
),
KEY_HUMIDITY: SensorDescription(
device_key=KEY_HUMIDITY,
device_class=DeviceClass.HUMIDITY,
native_unit_of_measurement="%",
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_TEMPERATURE: SensorValue(
name="Temperature", device_key=KEY_TEMPERATURE, native_value=23.5
),
KEY_HUMIDITY: SensorValue(
name="Humidity", device_key=KEY_HUMIDITY, native_value=48.7
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
def test_Xiaomi_MJYD02YL():
"""Test Xiaomi parser for MJYD02YL."""
def test_Xiaomi_MUE4094RT():
"""Test Xiaomi parser for MUE4094RT."""
# MUE4094RT only sends motion detected as an event.
data_string = b"@0\xdd\x03$\x03\x00\x01\x01"
advertisement = bytes_to_service_info(data_string, address="DE:70:E8:B2:39:0C")
device = XiaomiBluetoothDeviceData()
assert device.supported(advertisement)
assert not device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Nightlight 390C (MUE4094RT)",
devices={
None: SensorDeviceInfo(
name="Nightlight 390C",
manufacturer="Xiaomi",
model="MUE4094RT",
hw_version=None,
sw_version="Xiaomi (MiBeacon V3)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
events={
KEY_EVENT_MOTION: Event(
device_key=KEY_EVENT_MOTION,
name="Motion",
event_type="motion_detected",
event_properties=None,
),
},
)
def test_Xiaomi_CGPR1():
"""Test Xiaomi parser for CGPR1 (motion + illuminance in lux)."""
# Unencrypted MiBeacon V4: product_id 0x0A83, obj000f carrying 100 lux.
# CGPR1 takes the device-specific obj000f branch that reports the raw
# illuminance value as lux (distinct from the MJYD02YL/RTCGQ02LM
# light *binary* sensor path).
data_string = b"P@\x83\n\x01\x83\x00\n8\xc1\xa4\x0f\x00\x03d\x00\x00"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:0A:00:83")
device = XiaomiBluetoothDeviceData()
assert device.supported(advertisement)
assert not device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Motion/Light Sensor 0083 (CGPR1)",
devices={
None: SensorDeviceInfo(
name="Motion/Light Sensor 0083",
manufacturer="Xiaomi",
model="CGPR1",
hw_version=None,
sw_version="Xiaomi (MiBeacon V4)",
)
},
entity_descriptions={
KEY_ILLUMINANCE: SensorDescription(
device_key=KEY_ILLUMINANCE,
device_class=DeviceClass.ILLUMINANCE,
native_unit_of_measurement=Units.LIGHT_LUX,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_ILLUMINANCE: SensorValue(
name="Illuminance", device_key=KEY_ILLUMINANCE, native_value=100
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_BINARY_MOTION: BinarySensorDescription(
device_key=KEY_BINARY_MOTION,
device_class=BinarySensorDeviceClass.MOTION,
),
},
binary_entity_values={
KEY_BINARY_MOTION: BinarySensorValue(
device_key=KEY_BINARY_MOTION, name="Motion", native_value=True
),
},
)
def test_Xiaomi_MMC_T201_1():
"""Test Xiaomi parser for MMC-T201-1."""
data_string = b'p"\xdb\x00o\xc1o\xdd\xf9\x81\x00\t\x00 \x05\xc6\rc\rQ'
advertisement = bytes_to_service_info(data_string, address="00:81:F9:DD:6F:C1")
device = XiaomiBluetoothDeviceData()
assert device.supported(advertisement)
assert not device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Baby Thermometer 6FC1 (MMC-T201-1)",
devices={
None: SensorDeviceInfo(
name="Baby Thermometer 6FC1",
manufacturer="Xiaomi",
model="MMC-T201-1",
hw_version=None,
sw_version="Xiaomi (MiBeacon V2)",
)
},
entity_descriptions={
KEY_TEMPERATURE: SensorDescription(
device_key=KEY_TEMPERATURE,
device_class=DeviceClass.TEMPERATURE,
native_unit_of_measurement="°C",
),
KEY_BATTERY: SensorDescription(
device_key=KEY_BATTERY,
device_class=DeviceClass.BATTERY,
native_unit_of_measurement="%",
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_BATTERY: SensorValue(
name="Battery", device_key=KEY_BATTERY, native_value=81
),
KEY_TEMPERATURE: SensorValue(
name="Temperature",
device_key=KEY_TEMPERATURE,
native_value=36.87199806168224,
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
def test_Xiaomi_MMC_W505():
"""Test Xiaomi parser for MMC-W505."""
data_string = b'p"\x91\x03\x0f\xdb\xabS\x18$\xd0\t\n\x00\x02u\r\x07'
advertisement = bytes_to_service_info(data_string, address="D0:24:18:53:AB:DB")
device = XiaomiBluetoothDeviceData()
assert device.supported(advertisement)
assert not device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Body Thermometer ABDB (MMC-W505)",
devices={
None: SensorDeviceInfo(
name="Body Thermometer ABDB",
manufacturer="Xiaomi",
model="MMC-W505",
hw_version=None,
sw_version="Xiaomi (MiBeacon V2)",
)
},
entity_descriptions={
KEY_TEMPERATURE: SensorDescription(
device_key=KEY_TEMPERATURE,
device_class=DeviceClass.TEMPERATURE,
native_unit_of_measurement="°C",
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_TEMPERATURE: SensorValue(
name="Temperature",
device_key=KEY_TEMPERATURE,
native_value=34.45,
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
def test_Xiaomi_M1S_T500():
"""Test Xiaomi parser for M1S-T500."""
data_string = b"q0\x89\x047\x11[\x17Cq\xe6\t\x10\x00\x02\x00\x03"
advertisement = bytes_to_service_info(data_string, address="E6:71:43:17:5B:11")
device = XiaomiBluetoothDeviceData()
assert device.supported(advertisement)
assert not device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Smart Toothbrush 5B11 (M1S-T500)",
devices={
None: SensorDeviceInfo(
name="Smart Toothbrush 5B11",
manufacturer="Xiaomi",
model="M1S-T500",
hw_version=None,
sw_version="Xiaomi (MiBeacon V3)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
KEY_COUNTER: SensorDescription(
device_key=KEY_COUNTER,
device_class=ExtendedSensorDeviceClass.COUNTER,
native_unit_of_measurement=None,
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
KEY_COUNTER: SensorValue(
name="Counter", device_key=KEY_COUNTER, native_value=3
),
},
binary_entity_descriptions={
KEY_BINARY_TOOTHBRUSH: BinarySensorDescription(
device_key=KEY_BINARY_TOOTHBRUSH,
device_class=ExtendedBinarySensorDeviceClass.TOOTHBRUSH,
),
},
binary_entity_values={
KEY_BINARY_TOOTHBRUSH: BinarySensorValue(
device_key=KEY_BINARY_TOOTHBRUSH, name="Toothbrush", native_value=True
),
},
)
def test_Xiaomi_T700():
"""Test Xiaomi parser for T700."""
bindkey = "1330b99cded13258acc391627e9771f7"
data_string = (
b"\x48\x58\x06\x08\xc9H\x0e\xf1\x12\x81\x07\x973\xfc\x14\x00\x00VD\xdbA"
)
advertisement = bytes_to_service_info(data_string, address="ED:DE:34:3F:48:0C")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Smart Toothbrush 480C (T700)",
devices={
None: SensorDeviceInfo(
name="Smart Toothbrush 480C",
manufacturer="Xiaomi",
model="T700",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
KEY_SCORE: SensorDescription(
device_key=KEY_SCORE,
device_class=ExtendedSensorDeviceClass.SCORE,
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
KEY_SCORE: SensorValue(name="Score", device_key=KEY_SCORE, native_value=83),
},
binary_entity_descriptions={
KEY_BINARY_TOOTHBRUSH: BinarySensorDescription(
device_key=KEY_BINARY_TOOTHBRUSH,
device_class=ExtendedBinarySensorDeviceClass.TOOTHBRUSH,
),
},
binary_entity_values={
KEY_BINARY_TOOTHBRUSH: BinarySensorValue(
device_key=KEY_BINARY_TOOTHBRUSH, name="Toothbrush", native_value=False
),
},
)
def test_Xiaomi_ZNMS16LM_fingerprint():
"""Test Xiaomi parser for ZNMS16LM."""
data_string = (
b"PD\x9e\x06B\x91\x8a\xebD\x1f\xd7" b"\x06\x00\x05\xff\xff\xff\xff\x00"
)
advertisement = bytes_to_service_info(data_string, address="D7:1F:44:EB:8A:91")
device = XiaomiBluetoothDeviceData()
assert device.supported(advertisement)
assert not device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Door Lock 8A91 (ZNMS16LM)",
devices={
None: SensorDeviceInfo(
name="Door Lock 8A91",
manufacturer="Xiaomi",
model="ZNMS16LM",
hw_version=None,
sw_version="Xiaomi (MiBeacon V4)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
KEY_KEY_ID: SensorDescription(
device_key=KEY_KEY_ID,
device_class=ExtendedSensorDeviceClass.KEY_ID,
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
KEY_KEY_ID: SensorValue(
name="Key id", device_key=KEY_KEY_ID, native_value="unknown operator"
),
},
binary_entity_descriptions={
KEY_BINARY_FINGERPRINT: BinarySensorDescription(
device_key=KEY_BINARY_FINGERPRINT,
device_class=ExtendedBinarySensorDeviceClass.FINGERPRINT,
),
},
binary_entity_values={
KEY_BINARY_FINGERPRINT: BinarySensorValue(
device_key=KEY_BINARY_FINGERPRINT, name="Fingerprint", native_value=True
),
},
events={
KEY_EVENT_FINGERPRINT: Event(
device_key=KEY_EVENT_FINGERPRINT,
name="Fingerprint",
event_type="match_successful",
event_properties=None,
),
},
)
def test_Xiaomi_ZNMS16LM_fingerprint_low_quality():
"""Test ZNMS16LM low-quality fingerprint result (match_byte 0x03)."""
data_string = (
b"PD\x9e\x06B\x91\x8a\xebD\x1f\xd7" b"\x06\x00\x05\x00\x00\x00\x00\x03"
)
advertisement = bytes_to_service_info(data_string, address="D7:1F:44:EB:8A:91")
device = XiaomiBluetoothDeviceData()
assert device.supported(advertisement)
assert not device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Door Lock 8A91 (ZNMS16LM)",
devices={
None: SensorDeviceInfo(
name="Door Lock 8A91",
manufacturer="Xiaomi",
model="ZNMS16LM",
hw_version=None,
sw_version="Xiaomi (MiBeacon V4)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
KEY_KEY_ID: SensorDescription(
device_key=KEY_KEY_ID,
device_class=ExtendedSensorDeviceClass.KEY_ID,
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
KEY_KEY_ID: SensorValue(
name="Key id", device_key=KEY_KEY_ID, native_value="administrator"
),
},
binary_entity_descriptions={
KEY_BINARY_FINGERPRINT: BinarySensorDescription(
device_key=KEY_BINARY_FINGERPRINT,
device_class=ExtendedBinarySensorDeviceClass.FINGERPRINT,
),
},
binary_entity_values={
KEY_BINARY_FINGERPRINT: BinarySensorValue(
device_key=KEY_BINARY_FINGERPRINT,
name="Fingerprint",
native_value=False,
),
},
events={
KEY_EVENT_FINGERPRINT: Event(
device_key=KEY_EVENT_FINGERPRINT,
name="Fingerprint",
event_type="low_quality_too_light_fuzzy",
event_properties=None,
),
},
)
def test_Xiaomi_ZNMS16LM_lock():
"""Test Xiaomi parser for ZNMS16LM."""
data_string = b"PD\x9e\x06C\x91\x8a\xebD\x1f\xd7\x0b\x00\t" b" \x02\x00\x01\x80|D/a"
advertisement = bytes_to_service_info(data_string, address="D7:1F:44:EB:8A:91")
device = XiaomiBluetoothDeviceData()
assert device.supported(advertisement)
assert not device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Door Lock 8A91 (ZNMS16LM)",
devices={
None: SensorDeviceInfo(
name="Door Lock 8A91",
manufacturer="Xiaomi",
model="ZNMS16LM",
hw_version=None,
sw_version="Xiaomi (MiBeacon V4)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
KEY_LOCK_METHOD: SensorDescription(
device_key=KEY_LOCK_METHOD,
device_class=ExtendedSensorDeviceClass.LOCK_METHOD,
),
KEY_KEY_ID: SensorDescription(
device_key=KEY_KEY_ID,
device_class=ExtendedSensorDeviceClass.KEY_ID,
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
KEY_LOCK_METHOD: SensorValue(
name="Lock method",
device_key=KEY_LOCK_METHOD,
native_value="biometrics",
),
KEY_KEY_ID: SensorValue(
name="Key id",
device_key=KEY_KEY_ID,
native_value="Fingerprint key id 2",
),
},
binary_entity_descriptions={
KEY_BINARY_LOCK: BinarySensorDescription(
device_key=KEY_BINARY_LOCK,
device_class=BinarySensorDeviceClass.LOCK,
),
},
binary_entity_values={
KEY_BINARY_LOCK: BinarySensorValue(
device_key=KEY_BINARY_LOCK, name="Lock", native_value=True
),
},
events={
DeviceKey(key="lock", device_id=None): Event(
device_key=DeviceKey(key="lock", device_id=None),
name="Lock",
event_type="unlock_outside_the_door",
event_properties=None,
),
},
)
def test_Xiaomi_Lockin_SV40_lock():
"""Test Xiaomi parser for Locking SV40."""
bindkey = "54d84797cb77f9538b224b305c877d1e"
data_string = (
b"\x48\x55\xc2\x11\x16\x50\x68\xb6\xfe\x3c\x87"
b"\x80\x95\xc8\xa5\x83\x4f\x00\x00\x00\x46\x32\x21\xc6"
)
advertisement = bytes_to_service_info(data_string, address="98:0C:33:A3:04:3D")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Door Lock 043D (Lockin-SV40)",
devices={
None: SensorDeviceInfo(
name="Door Lock 043D",
manufacturer="Xiaomi",
model="Lockin-SV40",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
KEY_LOCK_METHOD: SensorDescription(
device_key=KEY_LOCK_METHOD,
device_class=ExtendedSensorDeviceClass.LOCK_METHOD,
),
KEY_KEY_ID: SensorDescription(
device_key=KEY_KEY_ID,
device_class=ExtendedSensorDeviceClass.KEY_ID,
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
KEY_LOCK_METHOD: SensorValue(
name="Lock method", device_key=KEY_LOCK_METHOD, native_value="automatic"
),
KEY_KEY_ID: SensorValue(
name="Key id", device_key=KEY_KEY_ID, native_value="administrator"
),
},
binary_entity_descriptions={
KEY_BINARY_LOCK: BinarySensorDescription(
device_key=KEY_BINARY_LOCK,
device_class=BinarySensorDeviceClass.LOCK,
),
},
binary_entity_values={
KEY_BINARY_LOCK: BinarySensorValue(
device_key=KEY_BINARY_LOCK, name="Lock", native_value=True
),
},
events={
DeviceKey(key="lock", device_id=None): Event(
device_key=DeviceKey(key="lock", device_id=None),
name="Lock",
event_type="unlock_inside_the_door",
event_properties=None,
),
},
)
def test_Xiaomi_Lockin_SV40_door():
"""Test Xiaomi parser for Locking SV40."""
bindkey = "54d84797cb77f9538b224b305c877d1e"
data_string = (
b"\x48\x55\xc2\x11\x14\x4e\x28\x70\x32"
b"\x76\xfc\xcd\x3d\x00\x00\x00\x80\xe7\x22\x80"
)
advertisement = bytes_to_service_info(data_string, address="98:0C:33:A3:04:3D")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Door Lock 043D (Lockin-SV40)",
devices={
None: SensorDeviceInfo(
name="Door Lock 043D",
manufacturer="Xiaomi",
model="Lockin-SV40",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_BINARY_DOOR: BinarySensorDescription(
device_key=KEY_BINARY_DOOR,
device_class=BinarySensorDeviceClass.DOOR,
),
KEY_BINARY_DOOR_LEFT_OPEN: BinarySensorDescription(
device_key=KEY_BINARY_DOOR_LEFT_OPEN,
device_class=ExtendedBinarySensorDeviceClass.DOOR_LEFT_OPEN,
),
KEY_BINARY_PRY_THE_DOOR: BinarySensorDescription(
device_key=KEY_BINARY_PRY_THE_DOOR,
device_class=ExtendedBinarySensorDeviceClass.PRY_THE_DOOR,
),
},
binary_entity_values={
KEY_BINARY_DOOR: BinarySensorValue(
device_key=KEY_BINARY_DOOR, name="Door", native_value=False
),
KEY_BINARY_DOOR_LEFT_OPEN: BinarySensorValue(
device_key=KEY_BINARY_DOOR_LEFT_OPEN,
name="Door left open",
native_value=False,
),
KEY_BINARY_PRY_THE_DOOR: BinarySensorValue(
device_key=KEY_BINARY_PRY_THE_DOOR,
name="Pry the door",
native_value=False,
),
},
)
def test_Xiaomi_YLAI003():
"""Test Xiaomi parser for YLAI003."""
def test_Xiaomi_YLYK01YL():
"""Test Xiaomi parser for YLYK01YL."""
data_string = b"P0S\x01?tP\xe9A$\xf8\x01\x10\x03\x00\x00\x00"
advertisement = bytes_to_service_info(data_string, address="F8:24:41:E9:50:74")
device = XiaomiBluetoothDeviceData()
assert device.supported(advertisement)
assert not device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Remote Control 5074 (YLYK01YL)",
devices={
None: SensorDeviceInfo(
name="Remote Control 5074",
manufacturer="Xiaomi",
model="YLYK01YL",
hw_version=None,
sw_version="Xiaomi (MiBeacon V3)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_POWER: BinarySensorDescription(
device_key=KEY_POWER,
device_class=BinarySensorDeviceClass.POWER,
),
},
binary_entity_values={
KEY_POWER: BinarySensorValue(
name="Power", device_key=KEY_POWER, native_value=True
),
},
events={
DeviceKey(key="button_on", device_id=None): Event(
device_key=DeviceKey(key="button_on", device_id=None),
name="Button On",
event_type="press",
event_properties=None,
),
},
)
def test_Xiaomi_YLYK01YL_FANCL():
"""Test Xiaomi parser for YLYK01YL-FANCL."""
def test_Xiaomi_YLYK01YL_VENFAN():
"""Test Xiaomi parser for YLYK01YL-VENFAN."""
def test_Xiaomi_YLYB01YL_BHFRC():
"""Test Xiaomi parser for YLYB01YL-BHFRC."""
def test_Xiaomi_YLKG07YL_press():
"""Test Xiaomi parser for YLKG07YL, YLKG08YL while pressing dimmer (no rotation)."""
bindkey = "b853075158487ca39a5b5ea9"
data_string = b"X0\xb6\x03\xd2\x8b\x98\xc5A$\xf8\xc3I\x14vu~\x00\x00\x00\x99"
advertisement = bytes_to_service_info(data_string, address="F8:24:41:C5:98:8B")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Dimmer Switch 988B (YLKG07YL/YLKG08YL)",
devices={
None: SensorDeviceInfo(
name="Dimmer Switch 988B",
manufacturer="Xiaomi",
model="YLKG07YL/YLKG08YL",
hw_version=None,
sw_version="Xiaomi (MiBeacon V3 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
events={
KEY_EVENT_DIMMER: Event(
device_key=KEY_EVENT_DIMMER,
name="Dimmer",
event_type="press",
event_properties={"number_of_presses": 1},
),
},
)
def test_Xiaomi_YLKG07YL_rotate():
"""Test Xiaomi parser for YLKG07YL, YLKG08YL while rotating dimmer."""
data_string = b"X0\xb6\x036\x8b\x98\xc5A$\xf8\x8b\xb8\xf2f" b"\x13Q\x00\x00\x00\xd6"
advertisement = bytes_to_service_info(data_string, address="F8:24:41:C5:98:8B")
bindkey = "b853075158487ca39a5b5ea9"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Dimmer Switch 988B (YLKG07YL/YLKG08YL)",
devices={
None: SensorDeviceInfo(
name="Dimmer Switch 988B",
manufacturer="Xiaomi",
model="YLKG07YL/YLKG08YL",
hw_version=None,
sw_version="Xiaomi (MiBeacon V3 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
events={
KEY_EVENT_DIMMER: Event(
device_key=KEY_EVENT_DIMMER,
name="Dimmer",
event_type="rotate_left",
event_properties={"steps": 1},
),
},
)
def test_Xiaomi_K9B():
"""Test Xiaomi parser for K9B."""
def test_Xiaomi_HS1BB_MI_obj4803():
"""Test Xiaomi parser for Linptech HS1BB(MI) battery (4803)."""
data_string = b"XY\xeb*\x9e\xe9\x8e\x058\xc1\xa4\xd0z\xd3\xe38\x00\x003c]\x10"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:05:8E:E9")
bindkey = "7475a4a77584401780ffc3ee62dd353c"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Motion Sensor 8EE9 (HS1BB(MI))",
devices={
None: SensorDeviceInfo(
name="Motion Sensor 8EE9",
manufacturer="Xiaomi",
model="HS1BB(MI)",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_BATTERY: SensorDescription(
device_key=KEY_BATTERY,
device_class=DeviceClass.BATTERY,
native_unit_of_measurement="%",
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_BATTERY: SensorValue(
name="Battery", device_key=KEY_BATTERY, native_value=100
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
def test_Xiaomi_HS1BB_MI_obj4818():
"""Test Xiaomi parser for Linptech HS1BB(MI) no motion time (4818)."""
data_string = b"XY\xeb*\xc1\xe9\x8e\x058\xc1\xa4\x07YS\x0f\x8d8\x00\x00\xb7zp\xf8"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:05:8E:E9")
bindkey = "7475a4a77584401780ffc3ee62dd353c"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Motion Sensor 8EE9 (HS1BB(MI))",
devices={
None: SensorDeviceInfo(
name="Motion Sensor 8EE9",
manufacturer="Xiaomi",
model="HS1BB(MI)",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_BINARY_MOTION: BinarySensorDescription(
device_key=KEY_BINARY_MOTION,
device_class=BinarySensorDeviceClass.MOTION,
),
},
binary_entity_values={
KEY_BINARY_MOTION: BinarySensorValue(
device_key=KEY_BINARY_MOTION, name="Motion", native_value=False
),
},
)
def test_Xiaomi_MS1BB_MI_obj4a08():
"""Test Xiaomi parser for Linptech HS1BB(MI) motion + illuminance (4a08)."""
data_string = b"HY\xeb*\xc2\xfc\xe0,\xa0\xb4:\xf28\x00\x00\xa2\xd9\xf0_"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:05:8E:E9")
bindkey = "7475a4a77584401780ffc3ee62dd353c"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Motion Sensor 8EE9 (HS1BB(MI))",
devices={
None: SensorDeviceInfo(
name="Motion Sensor 8EE9",
manufacturer="Xiaomi",
model="HS1BB(MI)",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_ILLUMINANCE: SensorDescription(
device_key=KEY_ILLUMINANCE,
device_class=DeviceClass.ILLUMINANCE,
native_unit_of_measurement=Units.LIGHT_LUX,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_ILLUMINANCE: SensorValue(
name="Illuminance", device_key=KEY_ILLUMINANCE, native_value=228
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_BINARY_MOTION: BinarySensorDescription(
device_key=KEY_BINARY_MOTION,
device_class=BinarySensorDeviceClass.MOTION,
),
},
binary_entity_values={
KEY_BINARY_MOTION: BinarySensorValue(
device_key=KEY_BINARY_MOTION, name="Motion", native_value=True
),
},
)
def test_Xiaomi_MS1BB_MI_obj4804():
"""Test Xiaomi parser for Linptech MS1BB(MI) with obj4804."""
data_string = b"XY\x89\x18\x9ag\xe5f8\xc1\xa4\x9d\xd9z\xf3&\x00\x00\xc8\xa6\x0b\xd5"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:66:E5:67")
bindkey = "0fdcc30fe9289254876b5ef7c11ef1f0"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Door/Window Sensor E567 (MS1BB(MI))",
devices={
None: SensorDeviceInfo(
name="Door/Window Sensor E567",
manufacturer="Xiaomi",
model="MS1BB(MI)",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_BINARY_OPENING: BinarySensorDescription(
device_key=KEY_BINARY_OPENING,
device_class=BinarySensorDeviceClass.OPENING,
),
},
binary_entity_values={
KEY_BINARY_OPENING: BinarySensorValue(
device_key=KEY_BINARY_OPENING, name="Opening", native_value=True
),
},
)
def test_Xiaomi_MS1BB_MI_obj4a12():
"""Test Xiaomi parser for Linptech MS1BB(MI) with obj4a12."""
data_string = b"XY\x89\x18ug\xe5f8\xc1\xa4i\xdd\xf3\xa1&\x00\x00\xa2J\x1bE"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:66:E5:67")
bindkey = "0fdcc30fe9289254876b5ef7c11ef1f0"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Door/Window Sensor E567 (MS1BB(MI))",
devices={
None: SensorDeviceInfo(
name="Door/Window Sensor E567",
manufacturer="Xiaomi",
model="MS1BB(MI)",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_BINARY_OPENING: BinarySensorDescription(
device_key=KEY_BINARY_OPENING,
device_class=BinarySensorDeviceClass.OPENING,
),
KEY_BINARY_DOOR_LEFT_OPEN: BinarySensorDescription(
device_key=KEY_BINARY_DOOR_LEFT_OPEN,
device_class=ExtendedBinarySensorDeviceClass.DOOR_LEFT_OPEN,
),
KEY_BINARY_DEVICE_FORCIBLY_REMOVED: BinarySensorDescription(
device_key=KEY_BINARY_DEVICE_FORCIBLY_REMOVED,
device_class=ExtendedBinarySensorDeviceClass.DEVICE_FORCIBLY_REMOVED,
),
},
binary_entity_values={
KEY_BINARY_OPENING: BinarySensorValue(
device_key=KEY_BINARY_OPENING, name="Opening", native_value=False
),
KEY_BINARY_DOOR_LEFT_OPEN: BinarySensorValue(
device_key=KEY_BINARY_DOOR_LEFT_OPEN,
name="Door left open",
native_value=False,
),
KEY_BINARY_DEVICE_FORCIBLY_REMOVED: BinarySensorValue(
device_key=KEY_BINARY_DEVICE_FORCIBLY_REMOVED,
name="Device forcibly removed",
native_value=False,
),
},
)
def test_Xiaomi_MS1BB_MI_obj4a13():
"""Test Xiaomi parser for Linptech MS1BB(MI) with obj4a13."""
data_string = b"XY\x89\x18\x91g\xe5f8\xc1\xa4\xd6\x12\rm&\x00\x00o\xbc\x0c\xb4"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:66:E5:67")
bindkey = "0fdcc30fe9289254876b5ef7c11ef1f0"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Door/Window Sensor E567 (MS1BB(MI))",
devices={
None: SensorDeviceInfo(
name="Door/Window Sensor E567",
manufacturer="Xiaomi",
model="MS1BB(MI)",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
events={
KEY_EVENT_BUTTON: Event(
device_key=KEY_EVENT_BUTTON,
name="Button",
event_type="press",
event_properties=None,
),
},
)
def test_Xiaomi_RS1BB_MI_obj4806():
"""Test Xiaomi parser for Linptech RS1BB(MI) with obj4806."""
data_string = b"XY\x0f?JgL\xb98\xc1\xa4\xd6\xe5{\x83\x04\x00\x00\xd0\x1e\x0bK"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:B9:4C:67")
bindkey = "33ede53321bc73c790a8daae4581f3d5"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Flood and Rain Sensor 4C67 (RS1BB(MI))",
devices={
None: SensorDeviceInfo(
name="Flood and Rain Sensor 4C67",
manufacturer="Xiaomi",
model="RS1BB(MI)",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_MOISTURE: BinarySensorDescription(
device_key=KEY_MOISTURE,
device_class=BinarySensorDeviceClass.MOISTURE,
),
},
binary_entity_values={
KEY_MOISTURE: BinarySensorValue(
name="Moisture", device_key=KEY_MOISTURE, native_value=False
),
},
)
def test_Xiaomi_XMWXKG01YL():
"""Test Xiaomi parser for XMWXKG01YL Switch (double button)."""
data_string = b"XYI\x19Os\x12\x87\x83\xed\xdc\x0b48\n\x02\x00\x00\x8dI\xae("
advertisement = bytes_to_service_info(data_string, address="DC:ED:83:87:12:73")
bindkey = "b93eb3787eabda352edd94b667f5d5a9"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Switch (double button) 1273 (XMWXKG01YL)",
devices={
None: SensorDeviceInfo(
name="Switch (double button) 1273",
manufacturer="Xiaomi",
model="XMWXKG01YL",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
events={
DeviceKey(key="button_right", device_id=None): Event(
device_key=DeviceKey(key="button_right", device_id=None),
name="Button Right",
event_type="press",
event_properties=None,
),
},
)
def test_Xiaomi_XMZNMS08LM_door():
"""Test Xiaomi parser for XMZNMS08LM."""
bindkey = "2c3795afa33019a8afdc17ba99e6f217"
data_string = b"HU9\x0e3\x9cq\xc0$\x1f\xff\xee\x80S\x00\x00\x02\xb4\xc59"
advertisement = bytes_to_service_info(data_string, address="EE:89:73:44:BE:98")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Door Lock BE98 (XMZNMS08LM)",
devices={
None: SensorDeviceInfo(
name="Door Lock BE98",
manufacturer="Xiaomi",
model="XMZNMS08LM",
sw_version="Xiaomi (MiBeacon V5 encrypted)",
hw_version=None,
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_BINARY_DOOR: BinarySensorDescription(
device_key=KEY_BINARY_DOOR,
device_class=BinarySensorDeviceClass.DOOR,
),
KEY_BINARY_DOOR_LEFT_OPEN: BinarySensorDescription(
device_key=KEY_BINARY_DOOR_LEFT_OPEN,
device_class=ExtendedBinarySensorDeviceClass.DOOR_LEFT_OPEN,
),
KEY_BINARY_PRY_THE_DOOR: BinarySensorDescription(
device_key=KEY_BINARY_PRY_THE_DOOR,
device_class=ExtendedBinarySensorDeviceClass.PRY_THE_DOOR,
),
},
binary_entity_values={
KEY_BINARY_DOOR: BinarySensorValue(
device_key=KEY_BINARY_DOOR, name="Door", native_value=False
),
KEY_BINARY_DOOR_LEFT_OPEN: BinarySensorValue(
device_key=KEY_BINARY_DOOR_LEFT_OPEN,
name="Door left open",
native_value=False,
),
KEY_BINARY_PRY_THE_DOOR: BinarySensorValue(
device_key=KEY_BINARY_PRY_THE_DOOR,
name="Pry the door",
native_value=False,
),
},
)
def test_Xiaomi_XMZNMS08LM_lock():
"""Test Xiaomi parser for XMZNMS08LM."""
bindkey = "2c3795afa33019a8afdc17ba99e6f217"
data_string = (
b"\x48\x55\x39\x0e\x2f\xdf\x9d\x3f\xdd\x9a\x66\x37"
b"\x13\x15\x29\xf8\x7b\x53\x00\x00\xbc\xc3\x40\x21"
)
advertisement = bytes_to_service_info(data_string, address="EE:89:73:44:BE:98")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Door Lock BE98 (XMZNMS08LM)",
devices={
None: SensorDeviceInfo(
name="Door Lock BE98",
manufacturer="Xiaomi",
model="XMZNMS08LM",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
KEY_LOCK_METHOD: SensorDescription(
device_key=KEY_LOCK_METHOD,
device_class=ExtendedSensorDeviceClass.LOCK_METHOD,
),
KEY_KEY_ID: SensorDescription(
device_key=KEY_KEY_ID,
device_class=ExtendedSensorDeviceClass.KEY_ID,
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
KEY_LOCK_METHOD: SensorValue(
name="Lock method", device_key=KEY_LOCK_METHOD, native_value="manual"
),
KEY_KEY_ID: SensorValue(
name="Key id", device_key=KEY_KEY_ID, native_value="administrator"
),
},
binary_entity_descriptions={
KEY_BINARY_LOCK: BinarySensorDescription(
device_key=KEY_BINARY_LOCK,
device_class=BinarySensorDeviceClass.LOCK,
),
},
binary_entity_values={
KEY_BINARY_LOCK: BinarySensorValue(
device_key=KEY_BINARY_LOCK, name="Lock", native_value=True
),
},
events={
DeviceKey(key="lock", device_id=None): Event(
device_key=DeviceKey(key="lock", device_id=None),
name="Lock",
event_type="unlock_inside_the_door",
event_properties=None,
),
},
)
def test_Xiaomi_HS1BB_battery():
"""Test Xiaomi parser for HS1BB battery."""
data_string = b"XY\xeb*\x9e\xe9\x8e\x058\xc1\xa4\xd0z\xd3\xe38\x00\x003c]\x10"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:05:8E:E9")
bindkey = "7475a4a77584401780ffc3ee62dd353c"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Motion Sensor 8EE9 (HS1BB(MI))",
devices={
None: SensorDeviceInfo(
name="Motion Sensor 8EE9",
manufacturer="Xiaomi",
model="HS1BB(MI)",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_BATTERY: SensorDescription(
device_key=KEY_BATTERY,
device_class=DeviceClass.BATTERY,
native_unit_of_measurement="%",
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_BATTERY: SensorValue(
name="Battery", device_key=KEY_BATTERY, native_value=100
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
)
def test_Xiaomi_HS1BB_no_motion():
"""Test Xiaomi parser for HS1BB."""
data_string = b"XY\xeb*\xc1\xe9\x8e\x058\xc1\xa4\x07YS\x0f\x8d8\x00\x00\xb7zp\xf8"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:05:8E:E9")
bindkey = "7475a4a77584401780ffc3ee62dd353c"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Motion Sensor 8EE9 (HS1BB(MI))",
devices={
None: SensorDeviceInfo(
name="Motion Sensor 8EE9",
manufacturer="Xiaomi",
model="HS1BB(MI)",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_BINARY_MOTION: BinarySensorDescription(
device_key=KEY_BINARY_MOTION,
device_class=BinarySensorDeviceClass.MOTION,
),
},
binary_entity_values={
KEY_BINARY_MOTION: BinarySensorValue(
device_key=KEY_BINARY_MOTION, name="Motion", native_value=False
),
},
)
def test_Xiaomi_HS1BB_illuminanca_and_motion():
"""Test Xiaomi parser for HS1BB illuminance and motion."""
data_string = b"HY\xeb*\xc2\xfc\xe0,\xa0\xb4:\xf28\x00\x00\xa2\xd9\xf0_"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:05:8E:E9")
bindkey = "7475a4a77584401780ffc3ee62dd353c"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Motion Sensor 8EE9 (HS1BB(MI))",
devices={
None: SensorDeviceInfo(
name="Motion Sensor 8EE9",
manufacturer="Xiaomi",
model="HS1BB(MI)",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_ILLUMINANCE: SensorDescription(
device_key=KEY_ILLUMINANCE,
device_class=DeviceClass.ILLUMINANCE,
native_unit_of_measurement="lx",
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_ILLUMINANCE: SensorValue(
name="Illuminance", device_key=KEY_ILLUMINANCE, native_value=228
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_BINARY_MOTION: BinarySensorDescription(
device_key=KEY_BINARY_MOTION,
device_class=BinarySensorDeviceClass.MOTION,
),
},
binary_entity_values={
KEY_BINARY_MOTION: BinarySensorValue(
device_key=KEY_BINARY_MOTION, name="Motion", native_value=True
),
},
)
def test_Xiaomi_DSL_C08():
"""Test Xiaomi parser for DSL-C08."""
def test_Xiaomi_RTCGQ02LM_light_and_motion():
"""Test Xiaomi parser for RTCGQ02LM."""
data_string = b"XY\x8d\n\x8cw\x8e <\xc2\x18Z'6(\xec2\x06\x00\x00\xc4&@\x15"
advertisement = bytes_to_service_info(data_string, address="18:C2:3C:20:8E:77")
bindkey = "4960bb9f8711b4ffd7df1756d11427ae"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.sleepy_device
assert device.update(advertisement) == SensorUpdate(
title="Motion Sensor 8E77 (RTCGQ02LM)",
devices={
None: SensorDeviceInfo(
name="Motion Sensor 8E77",
manufacturer="Xiaomi",
model="RTCGQ02LM",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_BINARY_MOTION: BinarySensorDescription(
device_key=KEY_BINARY_MOTION,
device_class=BinarySensorDeviceClass.MOTION,
),
KEY_BINARY_LIGHT: BinarySensorDescription(
device_key=KEY_BINARY_LIGHT,
device_class=BinarySensorDeviceClass.LIGHT,
),
},
binary_entity_values={
KEY_BINARY_MOTION: BinarySensorValue(
device_key=KEY_BINARY_MOTION, name="Motion", native_value=True
),
KEY_BINARY_LIGHT: BinarySensorValue(
device_key=KEY_BINARY_LIGHT, name="Light", native_value=True
),
},
)
def test_Xiaomi_RTCGQ02LM_timeout_motion():
"""Test Xiaomi parser for RTCGQ02LM."""
data_string = b"HY\x8d\n\x92ySu\x0f\xed\x0f\x99\x06\x00\x00\\\xad,)"
advertisement = bytes_to_service_info(data_string, address="18:C2:3C:20:8E:77")
bindkey = "4960bb9f8711b4ffd7df1756d11427ae"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Motion Sensor 8E77 (RTCGQ02LM)",
devices={
None: SensorDeviceInfo(
name="Motion Sensor 8E77",
manufacturer="Xiaomi",
model="RTCGQ02LM",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_BINARY_MOTION: BinarySensorDescription(
device_key=KEY_BINARY_MOTION,
device_class=BinarySensorDeviceClass.MOTION,
),
},
binary_entity_values={
KEY_BINARY_MOTION: BinarySensorValue(
device_key=KEY_BINARY_MOTION, name="Motion", native_value=False
),
},
)
def test_Xiaomi_XMPIRO2SXS():
"""Test Xiaomi parser for Xiaomi Human Body Sensor 2S XMPIRO2SXS."""
data_string = b"HY15\x0bdy\x91\x173\x1e\xf4\x02\x00\x00\xc5\xd2\xf6\xac"
advertisement = bytes_to_service_info(data_string, address="DC:8E:95:2D:EA:43")
bindkey = "685d647dc5e7bc9bcfcf5a1357bd2114"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Motion Sensor EA43 (XMPIRO2SXS)",
devices={
None: SensorDeviceInfo(
name="Motion Sensor EA43",
manufacturer="Xiaomi",
model="XMPIRO2SXS",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_ILLUMINANCE: SensorDescription(
device_key=KEY_ILLUMINANCE,
device_class=DeviceClass.ILLUMINANCE,
native_unit_of_measurement=Units.LIGHT_LUX,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_ILLUMINANCE: SensorValue(
name="Illuminance", device_key=KEY_ILLUMINANCE, native_value=51
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_BINARY_MOTION: BinarySensorDescription(
device_key=KEY_BINARY_MOTION,
device_class=BinarySensorDeviceClass.MOTION,
),
},
binary_entity_values={
KEY_BINARY_MOTION: BinarySensorValue(
device_key=KEY_BINARY_MOTION, name="Motion", native_value=True
),
},
)
def test_Xiaomi_XMOSB01XS_ILLUMINANCE():
"""Test Xiaomi parser for Xiaomi Occupancy(Human Presence) Sensor XMOSB01XS."""
data_string = (
b"\x48\x59\x83\x46\x0d\xdc\x21\x3c\xe9\x81\xda\x7a\xe2\x02\x00\x44\x41\xf8\x8c"
)
advertisement = bytes_to_service_info(data_string, address="0C:43:14:A1:41:1E")
bindkey = "0a4552cb19a639b72b8ed09bde6d5bfa"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.sleepy_device
assert device.update(advertisement) == SensorUpdate(
title="Occupancy Sensor 411E (XMOSB01XS)",
devices={
None: SensorDeviceInfo(
name="Occupancy Sensor 411E",
manufacturer="Xiaomi",
model="XMOSB01XS",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_ILLUMINANCE: SensorDescription(
device_key=KEY_ILLUMINANCE,
device_class=DeviceClass.ILLUMINANCE,
native_unit_of_measurement=Units.LIGHT_LUX,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_ILLUMINANCE: SensorValue(
name="Illuminance", device_key=KEY_ILLUMINANCE, native_value=38
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={},
binary_entity_values={},
)
def test_Xiaomi_XMOSB01XS_OCCUPANCY():
"""Test Xiaomi parser for Xiaomi Occupancy(Human Presence) Sensor XMOSB01XS."""
data_string = (
b"\x58\x59\x83\x46\x1f\xbd\xb1\xc4\x67\x48\xd4"
b"\x9d\x1e\xfd\x8c\x04\x00\x00\xe5\x7e\x87\x3a"
)
advertisement = bytes_to_service_info(data_string, address="D4:48:67:C4:B1:BD")
bindkey = "920ce119b34410d38251ccea54c0f915"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.sleepy_device
assert device.update(advertisement) == SensorUpdate(
title="Occupancy Sensor B1BD (XMOSB01XS)",
devices={
None: SensorDeviceInfo(
name="Occupancy Sensor B1BD",
manufacturer="Xiaomi",
model="XMOSB01XS",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_BINARY_OCCUPANCY: BinarySensorDescription(
device_key=KEY_BINARY_OCCUPANCY,
device_class=BinarySensorDeviceClass.OCCUPANCY,
),
},
binary_entity_values={
KEY_BINARY_OCCUPANCY: BinarySensorValue(
device_key=KEY_BINARY_OCCUPANCY, name="Occupancy", native_value=False
),
},
)
def test_Xiaomi_PTX_press():
"""Test Xiaomi parser for Xiaomi PTX YK1 QMIMB."""
bindkey = "a74510b40386d35ae6227a7451efc76e"
data_string = b"XY\xbb8\x04\xad\xb9\xa58\xc1\xa4\xdc\x10\xb5\x04\x00\x00,\x12/\xb6"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:A5:B9:AD")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Wireless Switch B9AD (PTX_YK1_QMIMB)",
devices={
None: SensorDeviceInfo(
name="Wireless Switch B9AD",
manufacturer="Xiaomi",
model="PTX_YK1_QMIMB",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
events={
KEY_EVENT_BUTTON: Event(
device_key=KEY_EVENT_BUTTON,
name="Button",
event_type="press",
event_properties=None,
),
},
)
def test_Xiaomi_XMWXKG01LM_press():
"""Test Xiaomi parser for Xiaomi XMWXKG01LM ."""
bindkey = "7202a2d4201bbf82ea5bb3705657c32a"
data_string = b"XY\x87#5\x057$<\xc2\x18\xd6w\x94\x02\x00\x00\xcb-\xe3\t"
advertisement = bytes_to_service_info(data_string, address="18:C2:3C:24:37:05")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Button 3705 (XMWXKG01LM)",
devices={
None: SensorDeviceInfo(
name="Button 3705",
manufacturer="Xiaomi",
model="XMWXKG01LM",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
events={
KEY_EVENT_BUTTON: Event(
device_key=KEY_EVENT_BUTTON,
name="Button",
event_type="press",
event_properties=None,
),
},
)
def test_Xiaomi_Scale_S200_MJTZC02YM():
"""Test Xiaomi parser for Xiaomi Smart Scale S200 MJTZC02YM."""
data_string = (
b"HY\x04L\x01\x9a\x80\xa2u\x93\x90\x10\xf0\xab\xc4\xfa\xdc\x06\x00\x00=)\xc0D"
)
advertisement = bytes_to_service_info(data_string, address="D0:7B:6F:27:D7:29")
bindkey = "653b1b10e1cb35e4ac5e60fa45f3bf29"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
# assert device.sleepy_device
assert device.update(advertisement) == SensorUpdate(
title="Smart Scale S200 D729 (MJTZC02YM)",
devices={
None: SensorDeviceInfo(
name="Smart Scale S200 D729",
manufacturer="Xiaomi",
model="MJTZC02YM",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_MASS: SensorDescription(
device_key=KEY_MASS,
device_class=DeviceClass.MASS,
native_unit_of_measurement=Units.MASS_KILOGRAMS,
),
KEY_PROFILE_ID: SensorDescription(
device_key=KEY_PROFILE_ID,
device_class=ExtendedSensorDeviceClass.PROFILE_ID,
native_unit_of_measurement=None,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_MASS: SensorValue(
name="Mass",
device_key=KEY_MASS,
native_value=62.25,
),
KEY_PROFILE_ID: SensorValue(
name="Profile ID",
device_key=KEY_PROFILE_ID,
native_value=1,
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={},
binary_entity_values={},
)
def test_Xiaomi_Scale_S400_MJTZC01YM():
"""Test Xiaomi parser for Xiaomi Body Composition Scale S400 MJTZC01YM."""
data_string = b'HY\xd5;\n\xbc\x07\x8f\xf24\x8c\x84A8\xe90"\x00\x00\x00\x9eS\x85\x99'
advertisement = bytes_to_service_info(data_string, address="8C:D0:B2:F6:BE:EF")
bindkey = "0728974d657a4b60964c1b1677f35f7c"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.sleepy_device
assert device.update(advertisement) == SensorUpdate(
title="Body Composition Scale BEEF (MJTZC01YM)",
devices={
None: SensorDeviceInfo(
name="Body Composition Scale BEEF",
manufacturer="Xiaomi",
model="MJTZC01YM",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_MASS: SensorDescription(
device_key=KEY_MASS,
device_class=DeviceClass.MASS,
native_unit_of_measurement=Units.MASS_KILOGRAMS,
),
KEY_IMPEDANCE_LOW: SensorDescription(
device_key=KEY_IMPEDANCE_LOW,
device_class=ExtendedSensorDeviceClass.IMPEDANCE_LOW,
native_unit_of_measurement=Units.OHM,
),
KEY_HEART_RATE: SensorDescription(
device_key=KEY_HEART_RATE,
device_class=ExtendedSensorDeviceClass.HEART_RATE,
native_unit_of_measurement="bpm",
),
KEY_PROFILE_ID: SensorDescription(
device_key=KEY_PROFILE_ID,
device_class=ExtendedSensorDeviceClass.PROFILE_ID,
native_unit_of_measurement=None,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_MASS: SensorValue(
name="Mass",
device_key=KEY_MASS,
native_value=69.9,
),
KEY_IMPEDANCE_LOW: SensorValue(
name="Impedance Low",
device_key=KEY_IMPEDANCE_LOW,
native_value=543.2,
),
KEY_HEART_RATE: SensorValue(
name="Heart Rate",
device_key=KEY_HEART_RATE,
native_value=92,
),
KEY_PROFILE_ID: SensorValue(
name="Profile ID",
device_key=KEY_PROFILE_ID,
native_value=1,
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_STABILIZED: BinarySensorDescription(
device_key=KEY_STABILIZED,
device_class=ExtendedBinarySensorDeviceClass.STABILIZED,
),
},
binary_entity_values={
KEY_STABILIZED: BinarySensorValue(
name="Stabilized",
device_key=KEY_STABILIZED,
native_value=False,
),
},
)
def test_Xiaomi_Scale_S400_MJTZC01YM_packet_2():
"""Test Xiaomi parser for Xiaomi Body Composition Scale S400 MJTZC01YM
(second packet with only high frequency impedance).
"""
data_string = b"HY\xd5;\x0b\xd6\xef\x0b%\xdbrx^~/F\xd6\x00\x00\x00\xd8d-\xf6"
advertisement = bytes_to_service_info(data_string, address="8C:D0:B2:F6:BE:EF")
bindkey = "0728974d657a4b60964c1b1677f35f7c"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.sleepy_device
assert device.update(advertisement) == SensorUpdate(
title="Body Composition Scale BEEF (MJTZC01YM)",
devices={
None: SensorDeviceInfo(
name="Body Composition Scale BEEF",
manufacturer="Xiaomi",
model="MJTZC01YM",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_IMPEDANCE_HIGH: SensorDescription(
device_key=KEY_IMPEDANCE_HIGH,
device_class=ExtendedSensorDeviceClass.IMPEDANCE_HIGH,
native_unit_of_measurement=Units.OHM,
),
KEY_PROFILE_ID: SensorDescription(
device_key=KEY_PROFILE_ID,
device_class=ExtendedSensorDeviceClass.PROFILE_ID,
native_unit_of_measurement=None,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_IMPEDANCE_HIGH: SensorValue(
name="Impedance High",
device_key=KEY_IMPEDANCE_HIGH,
native_value=497.6,
),
KEY_PROFILE_ID: SensorValue(
name="Profile ID",
device_key=KEY_PROFILE_ID,
native_value=1,
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_STABILIZED: BinarySensorDescription(
device_key=KEY_STABILIZED,
device_class=ExtendedBinarySensorDeviceClass.STABILIZED,
),
},
binary_entity_values={
KEY_STABILIZED: BinarySensorValue(
name="Stabilized",
device_key=KEY_STABILIZED,
native_value=True,
),
},
)
def test_Xiaomi_XMWS01XS_press():
"""Test Xiaomi parser for XMWS01XS Switch (double button)."""
data_string = b"XYhYR\x11n _\xb9\x080&T\x8d\x02\x00\x00\xce1\xcf\xc3"
advertisement = bytes_to_service_info(data_string, address="08:B9:5F:20:6E:11")
bindkey = "59ba8eef0f5351bb09d6896762d5afa5"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Switch (double button) 6E11 (XMWS01XS)",
devices={
None: SensorDeviceInfo(
name="Switch (double button) 6E11",
manufacturer="Xiaomi",
model="XMWS01XS",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
events={
DeviceKey(key="button_left", device_id=None): Event(
device_key=DeviceKey(key="button_left", device_id=None),
name="Button Left",
event_type="press",
event_properties=None,
),
},
)
def test_Xiaomi_Scale_S400_MJTZC01YM_socks():
"""Test Xiaomi parser for S400 — measurement with socks (no impedance)."""
data_string = (
b"HY\xd5;\x71\x53\x04\x38\xb5\x89\x4b"
b"\x24\x2c\x20\x99\x08\xda\x00\x00\x00\x47\x9e\xcd\xa3"
)
advertisement = bytes_to_service_info(data_string, address="04:AE:47:67:C6:7C")
bindkey = "02d2900363ef629c736a4549677acbee"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Body Composition Scale C67C (MJTZC01YM)",
devices={
None: SensorDeviceInfo(
name="Body Composition Scale C67C",
manufacturer="Xiaomi",
model="MJTZC01YM",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_MASS: SensorDescription(
device_key=KEY_MASS,
device_class=DeviceClass.MASS,
native_unit_of_measurement=Units.MASS_KILOGRAMS,
),
KEY_PROFILE_ID: SensorDescription(
device_key=KEY_PROFILE_ID,
device_class=ExtendedSensorDeviceClass.PROFILE_ID,
native_unit_of_measurement=None,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_MASS: SensorValue(name="Mass", device_key=KEY_MASS, native_value=74.7),
KEY_PROFILE_ID: SensorValue(
name="Profile ID", device_key=KEY_PROFILE_ID, native_value=1
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_STABILIZED: BinarySensorDescription(
device_key=KEY_STABILIZED,
device_class=ExtendedBinarySensorDeviceClass.STABILIZED,
),
},
binary_entity_values={
KEY_STABILIZED: BinarySensorValue(
name="Stabilized", device_key=KEY_STABILIZED, native_value=True
),
},
)
def test_Xiaomi_Scale_S400_MJTZC01YM_reset():
"""Test Xiaomi parser for S400 — person stepped off scale."""
data_string = (
b"HY\xd5;\x72\x03\x6c\x67\x94\x35\x5a"
b"\x19\xdb\xc8\x64\xbf\xb3\x00\x00\x00\xe4\x15\x1d\xc8"
)
advertisement = bytes_to_service_info(data_string, address="04:AE:47:67:C6:7C")
bindkey = "02d2900363ef629c736a4549677acbee"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Body Composition Scale C67C (MJTZC01YM)",
devices={
None: SensorDeviceInfo(
name="Body Composition Scale C67C",
manufacturer="Xiaomi",
model="MJTZC01YM",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_PROFILE_ID: SensorDescription(
device_key=KEY_PROFILE_ID,
device_class=ExtendedSensorDeviceClass.PROFILE_ID,
native_unit_of_measurement=None,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_PROFILE_ID: SensorValue(
name="Profile ID", device_key=KEY_PROFILE_ID, native_value=1
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_STABILIZED: BinarySensorDescription(
device_key=KEY_STABILIZED,
device_class=ExtendedBinarySensorDeviceClass.STABILIZED,
),
},
binary_entity_values={
KEY_STABILIZED: BinarySensorValue(
name="Stabilized", device_key=KEY_STABILIZED, native_value=False
),
},
)
def test_Xiaomi_XMWS01XS_double_press():
"""Test Xiaomi parser for XMWS01XS Switch (double button)."""
data_string = b"XYhYQ\x11n _\xb9\x08\xd9\x1b\xd5\x85\x02\x00\x00\x07\xcc\xa8I"
advertisement = bytes_to_service_info(data_string, address="08:B9:5F:20:6E:11")
bindkey = "59ba8eef0f5351bb09d6896762d5afa5"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Switch (double button) 6E11 (XMWS01XS)",
devices={
None: SensorDeviceInfo(
name="Switch (double button) 6E11",
manufacturer="Xiaomi",
model="XMWS01XS",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
events={
DeviceKey(key="button_right", device_id=None): Event(
device_key=DeviceKey(key="button_right", device_id=None),
name="Button Right",
event_type="double_press",
event_properties=None,
),
},
)
def test_Xiaomi_XMWS01XS_long_press():
"""Test Xiaomi parser for XMWS01XS Switch (double button)."""
data_string = b"XYhYS\x11n _\xb9\x08\xf3xQ\x81\x02\x00\x00\xea\xc1\x83\xf4"
advertisement = bytes_to_service_info(data_string, address="08:B9:5F:20:6E:11")
bindkey = "59ba8eef0f5351bb09d6896762d5afa5"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Switch (double button) 6E11 (XMWS01XS)",
devices={
None: SensorDeviceInfo(
name="Switch (double button) 6E11",
manufacturer="Xiaomi",
model="XMWS01XS",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
events={
DeviceKey(key="button_right", device_id=None): Event(
device_key=DeviceKey(key="button_right", device_id=None),
name="Button Right",
event_type="long_press",
event_properties=None,
),
},
)
def test_can_create():
XiaomiBluetoothDeviceData()
def test_Xiaomi_ES3_illuminance():
"""Test Xiaomi parser for Linptech ES3 illuminance."""
data_string = b"HY\xfbP\xd9\x86\xd2~\x8fS\x13\xe9\x00\x00\x000\xadm\xa8"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:C7:C3:76")
bindkey = "b26295a7a08fbac306c8706ade7f0fe4"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.sleepy_device
assert device.update(advertisement) == SensorUpdate(
title="Human Presence Sensor C376 (ES3)",
devices={
None: SensorDeviceInfo(
name="Human Presence Sensor C376",
manufacturer="Linptech",
model="ES3",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_ILLUMINANCE: SensorDescription(
device_key=KEY_ILLUMINANCE,
device_class=DeviceClass.ILLUMINANCE,
native_unit_of_measurement=Units.LIGHT_LUX,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_ILLUMINANCE: SensorValue(
name="Illuminance", device_key=KEY_ILLUMINANCE, native_value=173
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={},
binary_entity_values={},
)
def test_Xiaomi_ES3_occupancy_on():
"""Test Xiaomi parser for Linptech ES3 occupancy detected."""
data_string = b"XY\xfbP\xdav\xc3\xc78\xc1\xa4\xaa\xbcL\x16\x00\x00\x00\xc6\x0c\x16F"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:C7:C3:76")
bindkey = "b26295a7a08fbac306c8706ade7f0fe4"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.sleepy_device
assert device.update(advertisement) == SensorUpdate(
title="Human Presence Sensor C376 (ES3)",
devices={
None: SensorDeviceInfo(
name="Human Presence Sensor C376",
manufacturer="Linptech",
model="ES3",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_BINARY_OCCUPANCY: BinarySensorDescription(
device_key=KEY_BINARY_OCCUPANCY,
device_class=BinarySensorDeviceClass.OCCUPANCY,
),
},
binary_entity_values={
KEY_BINARY_OCCUPANCY: BinarySensorValue(
device_key=KEY_BINARY_OCCUPANCY, name="Occupancy", native_value=True
),
},
)
def test_Xiaomi_ES3_occupancy_off():
"""Test Xiaomi parser for Linptech ES3 occupancy cleared."""
data_string = b"XY\xfbP2\x8a\x88\xa48\xc1\xa4E\x8a\x85\xb7\x96\x00\x00H\xfe\x13\xba"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:A4:88:8A")
bindkey = "fb352ea2139ab095877a9e2ae600c912"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.sleepy_device
assert device.update(advertisement) == SensorUpdate(
title="Human Presence Sensor 888A (ES3)",
devices={
None: SensorDeviceInfo(
name="Human Presence Sensor 888A",
manufacturer="Linptech",
model="ES3",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_BINARY_OCCUPANCY: BinarySensorDescription(
device_key=KEY_BINARY_OCCUPANCY,
device_class=BinarySensorDeviceClass.OCCUPANCY,
),
},
binary_entity_values={
KEY_BINARY_OCCUPANCY: BinarySensorValue(
device_key=KEY_BINARY_OCCUPANCY, name="Occupancy", native_value=False
),
},
)
def test_Xiaomi_KS1_double_press():
"""Test Xiaomi parser for KS1 quadruple-button double press (left button)."""
bindkey = "8bdff7d0f70fa7f5c68f42157b5fd65b"
data_string = bytes.fromhex("5859613ab4e6fea138c1a45905b5b90900006b8211cd")
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:A1:FE:E6")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Switch (quadruple button) FEE6 (KS1)",
devices={
None: SensorDeviceInfo(
name="Switch (quadruple button) FEE6",
manufacturer="Xiaomi",
model="KS1",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={},
binary_entity_values={},
events={
KEY_EVENT_BUTTON_LEFT: Event(
device_key=KEY_EVENT_BUTTON_LEFT,
name="Button Left",
event_type="double_press",
event_properties=None,
),
},
)
def test_Xiaomi_KS1_long_press():
"""Test Xiaomi parser for KS1 quadruple-button long press (left button)."""
bindkey = "8bdff7d0f70fa7f5c68f42157b5fd65b"
data_string = bytes.fromhex("5859613ab4e6fea138c1a45a05b5b9090000b43ff116")
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:A1:FE:E6")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Switch (quadruple button) FEE6 (KS1)",
devices={
None: SensorDeviceInfo(
name="Switch (quadruple button) FEE6",
manufacturer="Xiaomi",
model="KS1",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={},
binary_entity_values={},
events={
KEY_EVENT_BUTTON_LEFT: Event(
device_key=KEY_EVENT_BUTTON_LEFT,
name="Button Left",
event_type="long_press",
event_properties=None,
),
},
)
def test_Xiaomi_button_object_ignored_for_non_button_device():
"""A non-button device carrying a stray button object fires no event.
Exercises the device-type guard in obj560d/obj560e: any device that is not
KS1/KS1BP/KS2BB must ignore a 0x560d/0x560e object rather than emit a button
event. Here a LYWSDCGQ advert carries both obj560d and obj560e payloads; no
event results.
"""
bindkey = "8bdff7d0f70fa7f5c68f42157b5fd65b"
data_string = bytes.fromhex("5859aa01b4e6fea138c1a4ff47a4daba202c2f090000e761ffec")
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:A1:FE:E6")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
update = device.update(advertisement)
assert update.events == {}
def test_Xiaomi_KS2_button_press():
"""Test Xiaomi parser for Linptech KS2 button press."""
bindkey = "8bdff7d0f70fa7f5c68f42157b5fd65b"
data_string = (
b"XY\x0bR\xb4\xe6\xfe\xa1\x38\xc1\xa40\x8f\xa8\t\x00\x00\r\xe1\xeb\xdb"
)
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:A1:FE:E6")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Temperature/Humidity Sensor with Button FEE6 (KS2BB)",
devices={
None: SensorDeviceInfo(
name="Temperature/Humidity Sensor with Button FEE6",
manufacturer="Linptech",
model="KS2BB",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={},
binary_entity_values={},
events={
KEY_EVENT_BUTTON: Event(
device_key=KEY_EVENT_BUTTON,
name="Button",
event_type="press",
event_properties=None,
),
},
)
def test_Xiaomi_KS2_double_press():
"""Test Xiaomi parser for Linptech KS2 double button press."""
bindkey = "8bdff7d0f70fa7f5c68f42157b5fd65b"
data_string = bytes.fromhex("58590b52b4e6fea138c1a4318fa8090000ff75594b")
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:A1:FE:E6")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Temperature/Humidity Sensor with Button FEE6 (KS2BB)",
devices={
None: SensorDeviceInfo(
name="Temperature/Humidity Sensor with Button FEE6",
manufacturer="Linptech",
model="KS2BB",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={},
binary_entity_values={},
events={
KEY_EVENT_BUTTON: Event(
device_key=KEY_EVENT_BUTTON,
name="Button",
event_type="double_press",
event_properties=None,
),
},
)
def test_Xiaomi_KS2_long_press():
"""Test Xiaomi parser for Linptech KS2 long button press."""
bindkey = "8bdff7d0f70fa7f5c68f42157b5fd65b"
data_string = bytes.fromhex("58590b52b4e6fea138c1a4328fa8090000856f02bf")
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:A1:FE:E6")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Temperature/Humidity Sensor with Button FEE6 (KS2BB)",
devices={
None: SensorDeviceInfo(
name="Temperature/Humidity Sensor with Button FEE6",
manufacturer="Linptech",
model="KS2BB",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={},
binary_entity_values={},
events={
KEY_EVENT_BUTTON: Event(
device_key=KEY_EVENT_BUTTON,
name="Button",
event_type="long_press",
event_properties=None,
),
},
)
def test_Xiaomi_KS2_temperature():
"""Test Xiaomi parser for Linptech KS2 with temperature reading."""
bindkey = "8bdff7d0f70fa7f5c68f42157b5fd65b"
data_string = b"HY\x0bR\xb3\xc1\xb8\\P\xf3\\W\t\x00\x00J\x9e\xd6\xb8"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:A1:FE:E6")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Temperature/Humidity Sensor with Button FEE6 (KS2BB)",
devices={
None: SensorDeviceInfo(
name="Temperature/Humidity Sensor with Button FEE6",
manufacturer="Linptech",
model="KS2BB",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_TEMPERATURE: SensorDescription(
device_key=KEY_TEMPERATURE,
device_class=DeviceClass.TEMPERATURE,
native_unit_of_measurement=Units.TEMP_CELSIUS,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
),
},
entity_values={
KEY_TEMPERATURE: SensorValue(
name="Temperature", device_key=KEY_TEMPERATURE, native_value=26.2
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={},
binary_entity_values={},
events={},
)
def test_Xiaomi_KS2_humidity():
"""Test Xiaomi parser for Linptech KS2 with humidity reading."""
bindkey = "8bdff7d0f70fa7f5c68f42157b5fd65b"
data_string = b"XY\x0bRl\xe6\xfe\xa1\x38\xc1\xa4C\x05@g\x0b\x00\x00LDE\xf1"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:A1:FE:E6")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Temperature/Humidity Sensor with Button FEE6 (KS2BB)",
devices={
None: SensorDeviceInfo(
name="Temperature/Humidity Sensor with Button FEE6",
manufacturer="Linptech",
model="KS2BB",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_HUMIDITY: SensorDescription(
device_key=KEY_HUMIDITY,
device_class=DeviceClass.HUMIDITY,
native_unit_of_measurement=Units.PERCENTAGE,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
),
},
entity_values={
KEY_HUMIDITY: SensorValue(
name="Humidity", device_key=KEY_HUMIDITY, native_value=71
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={},
binary_entity_values={},
events={},
)
def test_Xiaomi_M1S_T500_score_and_counter():
"""Test Xiaomi parser for M1S-T500 toothbrush."""
# Battery - e1 09 0a 10 01 3e is the data. Mibeacon header is needed.
# 50 20 means V2, NO MAC, YES OBJECT (0x2050 -> 0010 0000 0101 0000).
# wait: let's use a standard V2 header that works.
# "P \xaa\x01\xda!" -> 50 20 aa 01 da 21
# 50 20 aa 01 da 21 (MAC) -> wait, if no MAC include, just 50 20
# Let's use 50 20 89 04 + Payload
# Actually, 0x0489 is Device ID for T500!
data_string_battery = bytes.fromhex("5020890401ffeeddccbbaa0a10013e")
advertisement_battery = bytes_to_service_info(
data_string_battery, address="AA:BB:CC:DD:EE:FF"
)
device1 = XiaomiBluetoothDeviceData()
assert device1.supported(advertisement_battery)
assert not device1.bindkey_verified
assert device1.update(advertisement_battery) == SensorUpdate(
title="Smart Toothbrush EEFF (M1S-T500)",
devices={
None: SensorDeviceInfo(
name="Smart Toothbrush EEFF",
manufacturer="Xiaomi",
model="M1S-T500",
hw_version=None,
sw_version="Xiaomi (MiBeacon V2)",
)
},
entity_descriptions={
KEY_BATTERY: SensorDescription(
device_key=KEY_BATTERY,
device_class=DeviceClass.BATTERY,
native_unit_of_measurement="%",
),
DeviceKey(key="voltage", device_id=None): SensorDescription(
device_key=DeviceKey(key="voltage", device_id=None),
device_class=DeviceClass.VOLTAGE,
native_unit_of_measurement="V",
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_BATTERY: SensorValue(
name="Battery", device_key=KEY_BATTERY, native_value=62
),
DeviceKey(key="voltage", device_id=None): SensorValue(
name="Voltage",
device_key=DeviceKey(key="voltage", device_id=None),
native_value=2.758,
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={},
binary_entity_values={},
events={},
)
data_string_score = bytes.fromhex("5020890401ffeeddccbbaa1000020146")
advertisement_score = bytes_to_service_info(
data_string_score, address="AA:BB:CC:DD:EE:FF"
)
device2 = XiaomiBluetoothDeviceData()
assert device2.supported(advertisement_score)
assert not device2.bindkey_verified
assert device2.update(advertisement_score) == SensorUpdate(
title="Smart Toothbrush EEFF (M1S-T500)",
devices={
None: SensorDeviceInfo(
name="Smart Toothbrush EEFF",
manufacturer="Xiaomi",
model="M1S-T500",
hw_version=None,
sw_version="Xiaomi (MiBeacon V2)",
)
},
entity_descriptions={
KEY_SCORE: SensorDescription(
device_key=KEY_SCORE,
device_class=ExtendedSensorDeviceClass.SCORE,
native_unit_of_measurement=None,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SCORE: SensorValue(name="Score", device_key=KEY_SCORE, native_value=70),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_BINARY_TOOTHBRUSH: BinarySensorDescription(
device_key=KEY_BINARY_TOOTHBRUSH,
device_class=ExtendedBinarySensorDeviceClass.TOOTHBRUSH,
),
},
binary_entity_values={
KEY_BINARY_TOOTHBRUSH: BinarySensorValue(
name="Toothbrush", device_key=KEY_BINARY_TOOTHBRUSH, native_value=False
),
},
events={},
)
data_string_counter = bytes.fromhex("5020890401ffeeddccbbaa1000020059")
advertisement_counter = bytes_to_service_info(
data_string_counter, address="AA:BB:CC:DD:EE:FF"
)
device3 = XiaomiBluetoothDeviceData()
assert device3.supported(advertisement_counter)
assert not device3.bindkey_verified
assert device3.update(advertisement_counter) == SensorUpdate(
title="Smart Toothbrush EEFF (M1S-T500)",
devices={
None: SensorDeviceInfo(
name="Smart Toothbrush EEFF",
manufacturer="Xiaomi",
model="M1S-T500",
hw_version=None,
sw_version="Xiaomi (MiBeacon V2)",
)
},
entity_descriptions={
KEY_COUNTER: SensorDescription(
device_key=KEY_COUNTER,
device_class=ExtendedSensorDeviceClass.COUNTER,
native_unit_of_measurement=None,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_COUNTER: SensorValue(
name="Counter", device_key=KEY_COUNTER, native_value=89
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_BINARY_TOOTHBRUSH: BinarySensorDescription(
device_key=KEY_BINARY_TOOTHBRUSH,
device_class=ExtendedBinarySensorDeviceClass.TOOTHBRUSH,
),
},
binary_entity_values={
KEY_BINARY_TOOTHBRUSH: BinarySensorValue(
name="Toothbrush", device_key=KEY_BINARY_TOOTHBRUSH, native_value=True
),
},
events={},
)
def test_Xiaomi_M1S_T500_state_only():
"""Test Xiaomi parser for M1S-T500 toothbrush state only (no score/counter)."""
# Toothbrush off (no score)
data_string_off = bytes.fromhex("5020890401ffeeddccbbaa10000101")
advertisement_off = bytes_to_service_info(
data_string_off, address="AA:BB:CC:DD:EE:FF"
)
device1 = XiaomiBluetoothDeviceData()
assert device1.supported(advertisement_off)
assert not device1.bindkey_verified
assert device1.update(advertisement_off) == SensorUpdate(
title="Smart Toothbrush EEFF (M1S-T500)",
devices={
None: SensorDeviceInfo(
name="Smart Toothbrush EEFF",
manufacturer="Xiaomi",
model="M1S-T500",
hw_version=None,
sw_version="Xiaomi (MiBeacon V2)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_BINARY_TOOTHBRUSH: BinarySensorDescription(
device_key=KEY_BINARY_TOOTHBRUSH,
device_class=ExtendedBinarySensorDeviceClass.TOOTHBRUSH,
),
},
binary_entity_values={
KEY_BINARY_TOOTHBRUSH: BinarySensorValue(
name="Toothbrush", device_key=KEY_BINARY_TOOTHBRUSH, native_value=False
),
},
events={},
)
# Toothbrush on (no counter)
data_string_on = bytes.fromhex("5020890401ffeeddccbbaa10000100")
advertisement_on = bytes_to_service_info(
data_string_on, address="AA:BB:CC:DD:EE:FF"
)
device2 = XiaomiBluetoothDeviceData()
assert device2.supported(advertisement_on)
assert not device2.bindkey_verified
assert device2.update(advertisement_on) == SensorUpdate(
title="Smart Toothbrush EEFF (M1S-T500)",
devices={
None: SensorDeviceInfo(
name="Smart Toothbrush EEFF",
manufacturer="Xiaomi",
model="M1S-T500",
hw_version=None,
sw_version="Xiaomi (MiBeacon V2)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_BINARY_TOOTHBRUSH: BinarySensorDescription(
device_key=KEY_BINARY_TOOTHBRUSH,
device_class=ExtendedBinarySensorDeviceClass.TOOTHBRUSH,
),
},
binary_entity_values={
KEY_BINARY_TOOTHBRUSH: BinarySensorValue(
name="Toothbrush", device_key=KEY_BINARY_TOOTHBRUSH, native_value=True
),
},
events={},
)
def test_Xiaomi_M2456B1_charging_state():
"""Test Xiaomi parser for M2456B1 Charging State."""
data_string = bytes.fromhex("4859fc59c32705ff45020000d6d291b3")
bindkey = "05eecf799ee981b3b73664e114b9373b"
advertisement = bytes_to_service_info(data_string, address="04:34:C3:75:6F:6D")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Smart Band 10 Ceramic Edition 6F6D (M2456B1)",
devices={
None: SensorDeviceInfo(
name="Smart Band 10 Ceramic Edition 6F6D",
manufacturer="Xiaomi",
model="M2456B1",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
),
},
entity_descriptions={
KEY_CHARGING_STATE: SensorDescription(
device_key=KEY_CHARGING_STATE,
device_class=ExtendedSensorDeviceClass.CHARGING_STATE,
native_unit_of_measurement=None,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_CHARGING_STATE: SensorValue(
device_key=KEY_CHARGING_STATE,
name="Charging State",
native_value="Full",
),
KEY_SIGNAL_STRENGTH: SensorValue(
device_key=KEY_SIGNAL_STRENGTH,
name="Signal Strength",
native_value=-60,
),
},
binary_entity_descriptions={},
binary_entity_values={},
events={},
)
def test_Xiaomi_M2456B1_sleep_state():
"""Test Xiaomi parser for M2456B1 Sleep State."""
data_string = bytes.fromhex("4859fc59da28bc61b4020000d935d001")
bindkey = "05eecf799ee981b3b73664e114b9373b"
advertisement = bytes_to_service_info(data_string, address="04:34:C3:75:6F:6D")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Smart Band 10 Ceramic Edition 6F6D (M2456B1)",
devices={
None: SensorDeviceInfo(
name="Smart Band 10 Ceramic Edition 6F6D",
manufacturer="Xiaomi",
model="M2456B1",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
),
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
device_key=KEY_SIGNAL_STRENGTH,
name="Signal Strength",
native_value=-60,
),
},
binary_entity_descriptions={
KEY_ASLEEP: BinarySensorDescription(
device_key=KEY_ASLEEP,
device_class=ExtendedBinarySensorDeviceClass.ASLEEP,
),
},
binary_entity_values={
KEY_ASLEEP: BinarySensorValue(
device_key=KEY_ASLEEP,
name="Asleep",
native_value=False,
),
},
events={},
)
def test_Xiaomi_M2456B1_device_wearing_status():
"""Test Xiaomi parser for M2456B1 Device Wearing Status."""
data_string = bytes.fromhex("4859fc59bf1c9fd59b020000084a4740")
bindkey = "05eecf799ee981b3b73664e114b9373b"
advertisement = bytes_to_service_info(data_string, address="04:34:C3:75:6F:6D")
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Smart Band 10 Ceramic Edition 6F6D (M2456B1)",
devices={
None: SensorDeviceInfo(
name="Smart Band 10 Ceramic Edition 6F6D",
manufacturer="Xiaomi",
model="M2456B1",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
),
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
device_key=KEY_SIGNAL_STRENGTH,
name="Signal Strength",
native_value=-60,
),
},
binary_entity_descriptions={
KEY_WEARING: BinarySensorDescription(
device_key=KEY_WEARING,
device_class=ExtendedBinarySensorDeviceClass.WEARING,
),
},
binary_entity_values={
KEY_WEARING: BinarySensorValue(
device_key=KEY_WEARING,
name="Wearing",
native_value=False,
),
},
events={},
)
def test_Xiaomi_ES5BB_illuminance():
"""Test Xiaomi parser for Linptech ES5 Illuminance."""
data_string = bytes.fromhex("48590f6abc7a1f20cfb0b0971c00009dad6cca")
advertisement = bytes_to_service_info(data_string, address="E8:B5:27:74:91:EE")
bindkey = "068a081b4e5aa7295ebf263585adf4f1"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.sleepy_device
assert device.update(advertisement) == SensorUpdate(
title="Human Presence Sensor 91EE (ES5BB)",
devices={
None: SensorDeviceInfo(
name="Human Presence Sensor 91EE",
manufacturer="Linptech",
model="ES5BB",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_ILLUMINANCE: SensorDescription(
device_key=KEY_ILLUMINANCE,
device_class=DeviceClass.ILLUMINANCE,
native_unit_of_measurement=Units.LIGHT_LUX,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_ILLUMINANCE: SensorValue(
name="Illuminance", device_key=KEY_ILLUMINANCE, native_value=8.0
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={},
binary_entity_values={},
)
def test_Xiaomi_ES5BB_occupancy():
"""Test Xiaomi parser for Linptech ES5 Occupancy."""
data_string = bytes.fromhex("58590f6a11ee917427b5e83567ac0e1c0000f681b610")
advertisement = bytes_to_service_info(data_string, address="E8:B5:27:74:91:EE")
bindkey = "068a081b4e5aa7295ebf263585adf4f1"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.sleepy_device
assert device.update(advertisement) == SensorUpdate(
title="Human Presence Sensor 91EE (ES5BB)",
devices={
None: SensorDeviceInfo(
name="Human Presence Sensor 91EE",
manufacturer="Linptech",
model="ES5BB",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_BINARY_OCCUPANCY: BinarySensorDescription(
device_key=KEY_BINARY_OCCUPANCY,
device_class=BinarySensorDeviceClass.OCCUPANCY,
),
},
binary_entity_values={
KEY_BINARY_OCCUPANCY: BinarySensorValue(
device_key=KEY_BINARY_OCCUPANCY, name="Occupancy", native_value=True
),
},
)
def test_Xiaomi_ES5BB_occupancy_close_range():
"""Test Xiaomi parser for Linptech ES5 Occupancy close range."""
data_string = bytes.fromhex("58590f6a14ee917427b5e8f139cdae1c00001bd88863")
advertisement = bytes_to_service_info(data_string, address="E8:B5:27:74:91:EE")
bindkey = "068a081b4e5aa7295ebf263585adf4f1"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.sleepy_device
assert device.update(advertisement) == SensorUpdate(
title="Human Presence Sensor 91EE (ES5BB)",
devices={
None: SensorDeviceInfo(
name="Human Presence Sensor 91EE",
manufacturer="Linptech",
model="ES5BB",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_BINARY_OCCUPANCY_CLOSE_RANGE: BinarySensorDescription(
device_key=KEY_BINARY_OCCUPANCY_CLOSE_RANGE,
device_class=BinarySensorDeviceClass.OCCUPANCY,
),
},
binary_entity_values={
KEY_BINARY_OCCUPANCY_CLOSE_RANGE: BinarySensorValue(
device_key=KEY_BINARY_OCCUPANCY_CLOSE_RANGE,
name="Occupancy close range",
native_value=True,
),
},
)
def test_Xiaomi_ES5BB_duration_detected():
"""Test Xiaomi parser for Linptech ES5 Duration detected."""
data_string = bytes.fromhex("58590f6abdee917427b5e885884d061c0000c263fbd0")
advertisement = bytes_to_service_info(data_string, address="E8:B5:27:74:91:EE")
bindkey = "068a081b4e5aa7295ebf263585adf4f1"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.sleepy_device
assert device.update(advertisement) == SensorUpdate(
title="Human Presence Sensor 91EE (ES5BB)",
devices={
None: SensorDeviceInfo(
name="Human Presence Sensor 91EE",
manufacturer="Linptech",
model="ES5BB",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
KEY_DURATION_DETECTED: SensorDescription(
device_key=KEY_DURATION_DETECTED,
device_class=ExtendedSensorDeviceClass.DURATION_DETECTED,
native_unit_of_measurement="min",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
KEY_DURATION_DETECTED: SensorValue(
device_key=KEY_DURATION_DETECTED,
name="Duration detected",
native_value=1,
),
},
)
def test_Xiaomi_ES5BB_duration_cleared():
"""Test Xiaomi parser for Linptech ES5 duration cleared."""
data_string = bytes.fromhex("58590f6ac1ee917427b5e8bb7b2c201c0000f252a862")
advertisement = bytes_to_service_info(data_string, address="E8:B5:27:74:91:EE")
bindkey = "068a081b4e5aa7295ebf263585adf4f1"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.sleepy_device
assert device.update(advertisement) == SensorUpdate(
title="Human Presence Sensor 91EE (ES5BB)",
devices={
None: SensorDeviceInfo(
name="Human Presence Sensor 91EE",
manufacturer="Linptech",
model="ES5BB",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
KEY_DURATION_CLEARED: SensorDescription(
device_key=KEY_DURATION_CLEARED,
device_class=ExtendedSensorDeviceClass.DURATION_CLEARED,
native_unit_of_measurement="min",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
KEY_DURATION_CLEARED: SensorValue(
device_key=KEY_DURATION_CLEARED,
name="Duration cleared",
native_value=1,
),
},
)
def test_Xiaomi_PS1BB_pressure_present_duration():
"""Test Xiaomi parser for Linptech PS1BB Pressure Present Duration."""
data_string = bytes.fromhex("59584c3f2012efbe38c1a4bbacc927535b030000004cdc48fd")
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:BE:EF:12")
bindkey = "8b72476b60fe2a0b63bf58d588fe4ea1"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.sleepy_device
assert device.update(advertisement) == SensorUpdate(
title="Seat Pressure Sensor EF12 (PS1BB)",
devices={
None: SensorDeviceInfo(
name="Seat Pressure Sensor EF12",
manufacturer="Linptech",
model="PS1BB",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
KEY_PRESSURE_PRESENT_DURATION: SensorDescription(
device_key=KEY_PRESSURE_PRESENT_DURATION,
device_class=ExtendedSensorDeviceClass.PRESSURE_PRESENT_DURATION,
native_unit_of_measurement="s",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength",
device_key=KEY_SIGNAL_STRENGTH,
native_value=-60,
),
KEY_PRESSURE_PRESENT_DURATION: SensorValue(
device_key=KEY_PRESSURE_PRESENT_DURATION,
name="Pressure present duration",
native_value=7800,
),
},
)
def test_Xiaomi_PS1BB_pressure_state_occupied():
"""Test Xiaomi parser for Linptech PS1BB pressure state occupied."""
data_string = bytes.fromhex("58594c3f1012efbe38c1a47f7b3e9500000035019b75")
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:BE:EF:12")
bindkey = "8b72476b60fe2a0b63bf58d588fe4ea1"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.sleepy_device
assert device.update(advertisement) == SensorUpdate(
title="Seat Pressure Sensor EF12 (PS1BB)",
devices={
None: SensorDeviceInfo(
name="Seat Pressure Sensor EF12",
manufacturer="Linptech",
model="PS1BB",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength",
device_key=KEY_SIGNAL_STRENGTH,
native_value=-60,
),
},
binary_entity_descriptions={
KEY_BINARY_OCCUPANCY: BinarySensorDescription(
device_key=KEY_BINARY_OCCUPANCY,
device_class=BinarySensorDeviceClass.OCCUPANCY,
),
},
binary_entity_values={
KEY_BINARY_OCCUPANCY: BinarySensorValue(
device_key=KEY_BINARY_OCCUPANCY,
name="Occupancy",
native_value=True,
),
},
)
def test_Xiaomi_PS1BB_pressure_state_clear():
"""Test Xiaomi parser for Linptech PS1BB pressure state clear."""
data_string = bytes.fromhex("58594c3f1312efbe38c1a418e36bcb000000e6fab951")
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:BE:EF:12")
bindkey = "8b72476b60fe2a0b63bf58d588fe4ea1"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.sleepy_device
assert device.update(advertisement) == SensorUpdate(
title="Seat Pressure Sensor EF12 (PS1BB)",
devices={
None: SensorDeviceInfo(
name="Seat Pressure Sensor EF12",
manufacturer="Linptech",
model="PS1BB",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength",
device_key=KEY_SIGNAL_STRENGTH,
native_value=-60,
),
},
binary_entity_descriptions={
KEY_BINARY_OCCUPANCY: BinarySensorDescription(
device_key=KEY_BINARY_OCCUPANCY,
device_class=BinarySensorDeviceClass.OCCUPANCY,
),
},
binary_entity_values={
KEY_BINARY_OCCUPANCY: BinarySensorValue(
device_key=KEY_BINARY_OCCUPANCY,
name="Occupancy",
native_value=False,
),
},
)
def test_Xiaomi_PS1BB_pressure_not_present_duration():
"""Test Xiaomi parser for Linptech PS1BB pressure not present duration."""
data_string = bytes.fromhex("59584c3f2112efbe38c1a4558db5277810330000001906bc63")
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:BE:EF:12")
bindkey = "8b72476b60fe2a0b63bf58d588fe4ea1"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.sleepy_device
assert device.update(advertisement) == SensorUpdate(
title="Seat Pressure Sensor EF12 (PS1BB)",
devices={
None: SensorDeviceInfo(
name="Seat Pressure Sensor EF12",
manufacturer="Linptech",
model="PS1BB",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
KEY_PRESSURE_NOT_PRESENT_DURATION: SensorDescription(
device_key=KEY_PRESSURE_NOT_PRESENT_DURATION,
device_class=ExtendedSensorDeviceClass.PRESSURE_NOT_PRESENT_DURATION,
native_unit_of_measurement="s",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength",
device_key=KEY_SIGNAL_STRENGTH,
native_value=-60,
),
KEY_PRESSURE_NOT_PRESENT_DURATION: SensorValue(
device_key=KEY_PRESSURE_NOT_PRESENT_DURATION,
name="Pressure not present duration",
native_value=120,
),
},
)
def test_Xiaomi_PS1BB_battery():
"""Test Xiaomi parser for Linptech PS1BB battery."""
data_string = bytes.fromhex("59584c3f2212efbe38c1a4da2cec2100000029c7c0c7")
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:BE:EF:12")
bindkey = "8b72476b60fe2a0b63bf58d588fe4ea1"
device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.sleepy_device
assert device.update(advertisement) == SensorUpdate(
title="Seat Pressure Sensor EF12 (PS1BB)",
devices={
None: SensorDeviceInfo(
name="Seat Pressure Sensor EF12",
manufacturer="Linptech",
model="PS1BB",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
KEY_BATTERY: SensorDescription(
device_key=KEY_BATTERY,
device_class=DeviceClass.BATTERY,
native_unit_of_measurement="%",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength",
device_key=KEY_SIGNAL_STRENGTH,
native_value=-60,
),
KEY_BATTERY: SensorValue(
device_key=KEY_BATTERY,
name="Battery",
native_value=100,
),
},
)