pax_global_header 0000666 0000000 0000000 00000000064 15235413126 0014514 g ustar 00root root 0000000 0000000 52 comment=d8e551c6e0c270431c4149782dd088b313e7cf2b
mopeka-iot-ble-1.0.0/ 0000775 0000000 0000000 00000000000 15235413126 0014317 5 ustar 00root root 0000000 0000000 mopeka-iot-ble-1.0.0/.all-contributorsrc 0000664 0000000 0000000 00000000455 15235413126 0020154 0 ustar 00root root 0000000 0000000 {
"projectName": "mopeka-iot-ble",
"projectOwner": "bluetooth-devices",
"repoType": "github",
"repoHost": "https://github.com",
"files": ["README.md"],
"imageSize": 80,
"commit": true,
"commitConvention": "angular",
"contributors": [],
"contributorsPerLine": 7,
"skipCi": true
}
mopeka-iot-ble-1.0.0/.copier-answers.yml 0000664 0000000 0000000 00000001010 15235413126 0020051 0 ustar 00root root 0000000 0000000 # Changes here will be overwritten by Copier
_commit: 9312d8b
_src_path: gh:browniebroke/pypackage-template
add_me_as_contributor: false
copyright_year: '2022'
documentation: false
email: bluetooth@koston.org
full_name: J. Nick Koston
github_username: bluetooth-devices
initial_commit: true
open_source_license: MIT
package_name: mopeka_iot_ble
project_name: Mopeka IOT BLE
project_short_description: Mopeka IOT BLE Parser
project_slug: mopeka-iot-ble
run_poetry_install: true
setup_github: true
setup_pre_commit: true
mopeka-iot-ble-1.0.0/.editorconfig 0000664 0000000 0000000 00000000444 15235413126 0016776 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
mopeka-iot-ble-1.0.0/.flake8 0000664 0000000 0000000 00000000056 15235413126 0015473 0 ustar 00root root 0000000 0000000 [flake8]
exclude = docs
max-line-length = 188
mopeka-iot-ble-1.0.0/.github/ 0000775 0000000 0000000 00000000000 15235413126 0015657 5 ustar 00root root 0000000 0000000 mopeka-iot-ble-1.0.0/.github/ISSUE_TEMPLATE/ 0000775 0000000 0000000 00000000000 15235413126 0020042 5 ustar 00root root 0000000 0000000 mopeka-iot-ble-1.0.0/.github/ISSUE_TEMPLATE/1-bug_report.md 0000664 0000000 0000000 00000000422 15235413126 0022670 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.
mopeka-iot-ble-1.0.0/.github/ISSUE_TEMPLATE/2-feature-request.md 0000664 0000000 0000000 00000000672 15235413126 0023651 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.
mopeka-iot-ble-1.0.0/.github/dependabot.yml 0000664 0000000 0000000 00000001015 15235413126 0020504 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: "pip" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
mopeka-iot-ble-1.0.0/.github/labels.toml 0000664 0000000 0000000 00000003515 15235413126 0020022 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"
mopeka-iot-ble-1.0.0/.github/workflows/ 0000775 0000000 0000000 00000000000 15235413126 0017714 5 ustar 00root root 0000000 0000000 mopeka-iot-ble-1.0.0/.github/workflows/ci.yml 0000664 0000000 0000000 00000005124 15235413126 0021034 0 ustar 00root root 0000000 0000000 name: CI
on:
push:
branches:
- main
pull_request:
concurrency:
group: ${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: "3.13"
- uses: pre-commit/action@v3.0.0
test:
strategy:
fail-fast: false
matrix:
python-version:
- "3.10"
- "3.11"
- "3.12"
- "3.13"
- "3.14"
os:
- ubuntu-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- uses: snok/install-poetry@v1.3.3
- name: Install Dependencies
run: poetry install
shell: bash
- name: Test with Pytest
run: poetry run pytest --cov-report=xml
shell: bash
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
release:
needs:
- test
- lint
if: github.ref_name == 'main'
runs-on: ubuntu-latest
environment: release
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
permissions:
id-token: write
contents: write
steps:
# Unlike pySwitchBot this repo has no default-branch ruleset, so PSR's
# version-bump commit/tag push works with the workflow token and no
# release-bot App token is needed.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
ref: ${{ github.ref }}
- name: Create local branch name
env:
BRANCH: ${{ github.ref_name }}
run: git switch -C "$BRANCH"
- name: Release
uses: python-semantic-release/python-semantic-release@39dd2052f2ce8282a5d932c31d58a2ca06d2550e # v10.6.1
id: release
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
if: steps.release.outputs.released == 'true'
- name: Publish package distributions to GitHub Releases
uses: python-semantic-release/upload-to-gh-release@0a92b5d7ebfc15a84f9801ebd1bf706343d43711 # main
if: steps.release.outputs.released == 'true'
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
mopeka-iot-ble-1.0.0/.github/workflows/hacktoberfest.yml 0000664 0000000 0000000 00000000534 15235413126 0023265 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.3.0
with:
github_token: ${{ secrets.GH_PAT }}
mopeka-iot-ble-1.0.0/.github/workflows/issue-manager.yml 0000664 0000000 0000000 00000001340 15235413126 0023175 0 ustar 00root root 0000000 0000000 name: Issue Manager
on:
schedule:
- cron: "0 0 * * *"
issue_comment:
types:
- created
issues:
types:
- labeled
pull_request_target:
types:
- labeled
workflow_dispatch:
jobs:
issue-manager:
runs-on: ubuntu-latest
steps:
- uses: tiangolo/issue-manager@0.4.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
config: >
{
"answered": {
"message": "Assuming the original issue was solved, it will be automatically closed now."
},
"waiting": {
"message": "Automatically closing. To re-open, please provide the additional information requested."
}
}
mopeka-iot-ble-1.0.0/.github/workflows/labels.yml 0000664 0000000 0000000 00000000774 15235413126 0021711 0 ustar 00root root 0000000 0000000 name: Sync Github labels
on:
push:
branches:
- main
paths:
- ".github/**"
jobs:
labels:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.8
- name: Install labels
run: pip install labels
- name: Sync config with Github
run: labels -u ${{ github.repository_owner }} -t ${{ secrets.GITHUB_TOKEN }} sync -f .github/labels.toml
mopeka-iot-ble-1.0.0/.github/workflows/pr-title.yml 0000664 0000000 0000000 00000002755 15235413126 0022210 0 ustar 00root root 0000000 0000000 name: Lint PR Title
# GitHub uses the pull request title as the commit subject when "Squash and
# merge" is used. When the repo is configured for squash-merge with the PR
# title as the commit title, individual commits on a PR branch are discarded
# by the squash, so this workflow — which validates the PR title against the
# Conventional Commits spec — is the sole enforcement point for the format of
# the commit that lands on main.
on:
pull_request_target:
types:
- opened
- edited
- synchronize
- reopened
permissions:
pull-requests: read
jobs:
lint:
name: Validate PR Title
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
# Conventional Commits types accepted by python-semantic-release.
types: |
build
chore
ci
docs
feat
fix
perf
refactor
revert
style
test
requireScope: false
# Subject (text after the type/scope) must start lowercase.
subjectPattern: ^(?![A-Z]).+$
subjectPatternError: |
The subject "{subject}" found in the pull request title "{title}"
didn't match the configured pattern. Please ensure that the subject
doesn't start with an uppercase character.
mopeka-iot-ble-1.0.0/.gitignore 0000664 0000000 0000000 00000004110 15235413126 0016303 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 {{package_name}} settings
.spyderproject
.spyproject
# Rope {{package_name}} 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/
mopeka-iot-ble-1.0.0/.gitpod.yml 0000664 0000000 0000000 00000000306 15235413126 0016405 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
mopeka-iot-ble-1.0.0/.idea/ 0000775 0000000 0000000 00000000000 15235413126 0015277 5 ustar 00root root 0000000 0000000 mopeka-iot-ble-1.0.0/.idea/mopeka-iot-ble.iml 0000664 0000000 0000000 00000000515 15235413126 0020610 0 ustar 00root root 0000000 0000000
mopeka-iot-ble-1.0.0/.idea/watcherTasks.xml 0000664 0000000 0000000 00000005253 15235413126 0020471 0 ustar 00root root 0000000 0000000
mopeka-iot-ble-1.0.0/.idea/workspace.xml 0000664 0000000 0000000 00000002736 15235413126 0020027 0 ustar 00root root 0000000 0000000
mopeka-iot-ble-1.0.0/.pre-commit-config.yaml 0000664 0000000 0000000 00000003457 15235413126 0020611 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|.copier-answers.yml"
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.17.0
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
- repo: https://github.com/python-poetry/poetry
rev: 2.4.1
hooks:
- id: poetry-check
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v4.0.0-alpha.8
hooks:
- id: prettier
args: ["--tab-width", "2"]
- repo: https://github.com/asottile/pyupgrade
rev: v3.21.2
hooks:
- id: pyupgrade
args: [--py310-plus]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.16.1
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- id: ruff-format
- repo: https://github.com/codespell-project/codespell
rev: v2.4.3
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.3.0
hooks:
- id: mypy
additional_dependencies: []
- repo: https://github.com/PyCQA/bandit
rev: 1.9.4
hooks:
- id: bandit
args: [-x, tests]
mopeka-iot-ble-1.0.0/CHANGELOG.md 0000664 0000000 0000000 00000006026 15235413126 0016134 0 ustar 00root root 0000000 0000000 # Changelog
## v0.8.0 (2024-07-27)
### Feature
* Add support for other fluids and air ([#21](https://github.com/Bluetooth-Devices/mopeka-iot-ble/issues/21)) ([`5c67178`](https://github.com/Bluetooth-Devices/mopeka-iot-ble/commit/5c671785665de3446622dc513932f271b6a176f0))
## v0.7.0 (2024-02-03)
### Feature
* Add TD40/TD200 models ([#15](https://github.com/Bluetooth-Devices/mopeka-iot-ble/issues/15)) ([`80615f6`](https://github.com/Bluetooth-Devices/mopeka-iot-ble/commit/80615f65ac780c64ff2a02716e7e61b4b8213f8b))
## v0.6.0 (2024-02-03)
### Feature
* Add support for Lippert BottleCheck ([#14](https://github.com/Bluetooth-Devices/mopeka-iot-ble/issues/14)) ([`95b10c2`](https://github.com/Bluetooth-Devices/mopeka-iot-ble/commit/95b10c299b082cc1b6047165e2ebaaab1647f071))
## v0.5.0 (2023-11-16)
### Feature
* Adding the new 2023 "Pro Check Universal" device (M1017020A) ([#9](https://github.com/Bluetooth-Devices/mopeka-iot-ble/issues/9)) ([`0b81c71`](https://github.com/Bluetooth-Devices/mopeka-iot-ble/commit/0b81c71c1b44382ec163d5db49902aa56c06bf7e))
## v0.4.1 (2023-02-23)
### Fix
* Show readings of quality 1 ([#5](https://github.com/Bluetooth-Devices/mopeka-iot-ble/issues/5)) ([`1e034e6`](https://github.com/Bluetooth-Devices/mopeka-iot-ble/commit/1e034e6c1884c7c8e1eefbe64022aaa11df68648))
## v0.4.0 (2023-01-24)
### Feature
* Express reading quality as a percentage ([#4](https://github.com/Bluetooth-Devices/mopeka-iot-ble/issues/4)) ([`1f26c88`](https://github.com/Bluetooth-Devices/mopeka-iot-ble/commit/1f26c88181963710aa7d427333536fb79236cfe6))
## v0.3.0 (2023-01-24)
### Feature
* Add support for the check device ([#3](https://github.com/Bluetooth-Devices/mopeka-iot-ble/issues/3)) ([`7637c02`](https://github.com/Bluetooth-Devices/mopeka-iot-ble/commit/7637c0237002e6325c6cc9c5d1ba45776b6760cc))
## v0.2.1 (2023-01-24)
### Fix
* Skip bad reads of the tank level ([#2](https://github.com/Bluetooth-Devices/mopeka-iot-ble/issues/2)) ([`a6cb356`](https://github.com/Bluetooth-Devices/mopeka-iot-ble/commit/a6cb356a8aec85d178e1092dae3e274394d6b0d0))
## v0.2.0 (2023-01-24)
### Feature
* Add more exports ([#1](https://github.com/Bluetooth-Devices/mopeka-iot-ble/issues/1)) ([`fe006f9`](https://github.com/Bluetooth-Devices/mopeka-iot-ble/commit/fe006f910ffe366c3f04cad09b83525d0c6399e4))
## v0.1.0 (2023-01-24)
### Feature
* Add cover ([`2032ee0`](https://github.com/Bluetooth-Devices/mopeka-iot-ble/commit/2032ee0993990d789b7efb831592b6c3b9ad8fc3))
* Init repo with sample data ([`124fa30`](https://github.com/Bluetooth-Devices/mopeka-iot-ble/commit/124fa30e0077ad1de26a8b128712345e17640028))
* Init repo with sample data ([`6395c16`](https://github.com/Bluetooth-Devices/mopeka-iot-ble/commit/6395c1621ac0bee757d3ffe4c28c71d9bbb347dd))
* Init repo with sample data ([`9b8e45c`](https://github.com/Bluetooth-Devices/mopeka-iot-ble/commit/9b8e45cff936b5e0fc4bc054511aede521a75985))
* Init repo with sample data ([`731d5ad`](https://github.com/Bluetooth-Devices/mopeka-iot-ble/commit/731d5ad7ffb715f9f63804c8be760836230d795c))
mopeka-iot-ble-1.0.0/CONTRIBUTING.md 0000664 0000000 0000000 00000007446 15235413126 0016563 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
Mopeka IOT BLE could always use more documentation, whether as part of the official Mopeka IOT 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/mopeka-iot-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/mopeka-iot-ble/issues
mopeka-iot-ble-1.0.0/LICENSE 0000664 0000000 0000000 00000002060 15235413126 0015322 0 ustar 00root root 0000000 0000000
MIT License
Copyright (c) 2022 J. Nick Koston
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.
mopeka-iot-ble-1.0.0/README.md 0000664 0000000 0000000 00000006406 15235413126 0015604 0 ustar 00root root 0000000 0000000 # Mopeka IOT BLE
Mopeka IOT BLE Parser
## Installation
Install this via pip (or your favourite package manager):
`pip install mopeka-iot-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
[Copier](https://copier.readthedocs.io/) and the
[browniebroke/pypackage-template](https://github.com/browniebroke/pypackage-template)
project template.
mopeka-iot-ble-1.0.0/commitlint.config.js 0000664 0000000 0000000 00000000364 15235413126 0020303 0 ustar 00root root 0000000 0000000 module.exports = {
extends: ["@commitlint/config-conventional"],
rules: {
"header-max-length": [0, "always", Infinity],
"body-max-line-length": [0, "always", Infinity],
"footer-max-line-length": [0, "always", Infinity],
},
};
mopeka-iot-ble-1.0.0/poetry.lock 0000664 0000000 0000000 00000126215 15235413126 0016522 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 = "bluetooth-data-tools"
version = "1.29.21"
description = "Tools for converting bluetooth data and packets"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "bluetooth_data_tools-1.29.21-cp314-cp314-manylinux_2_41_x86_64.whl", hash = "sha256:3a1de157f93da8e2d60ea5d92ada7e40e518f9bafce8a463ae882b92ec387155"},
{file = "bluetooth_data_tools-1.29.21.tar.gz", hash = "sha256:2ccf1e90212092cb6af825fd93f3f2f392c543fe2a3a894e7831cbc6f6851c15"},
]
[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.7.5"
description = "Models for storing and converting Bluetooth Sensor State Data"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "bluetooth_sensor_state_data-1.7.5-py3-none-any.whl", hash = "sha256:a4ce3cce9839422299209e5427a6ce24061ff844fbdb61af2a485e480b8d7e23"},
{file = "bluetooth_sensor_state_data-1.7.5.tar.gz", hash = "sha256:be9319a3d70745e11689e91c013bf71d0d5570d303a49d21668777ea7854227a"},
]
[package.dependencies]
home-assistant-bluetooth = ">=1.3.0"
sensor-state-data = ">=2.0"
[package.extras]
docs = ["Sphinx (>=5,<7)", "myst-parser (>=0.18,<3.1)", "sphinx-rtd-theme (>=1,<4)"]
[[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 = "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 = ["dev"]
markers = "sys_platform == \"win32\""
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[[package]]
name = "coverage"
version = "7.10.6"
description = "Code coverage measurement for Python"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "coverage-7.10.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:70e7bfbd57126b5554aa482691145f798d7df77489a177a6bef80de78860a356"},
{file = "coverage-7.10.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e41be6f0f19da64af13403e52f2dec38bbc2937af54df8ecef10850ff8d35301"},
{file = "coverage-7.10.6-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c61fc91ab80b23f5fddbee342d19662f3d3328173229caded831aa0bd7595460"},
{file = "coverage-7.10.6-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10356fdd33a7cc06e8051413140bbdc6f972137508a3572e3f59f805cd2832fd"},
{file = "coverage-7.10.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:80b1695cf7c5ebe7b44bf2521221b9bb8cdf69b1f24231149a7e3eb1ae5fa2fb"},
{file = "coverage-7.10.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2e4c33e6378b9d52d3454bd08847a8651f4ed23ddbb4a0520227bd346382bbc6"},
{file = "coverage-7.10.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c8a3ec16e34ef980a46f60dc6ad86ec60f763c3f2fa0db6d261e6e754f72e945"},
{file = "coverage-7.10.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7d79dabc0a56f5af990cc6da9ad1e40766e82773c075f09cc571e2076fef882e"},
{file = "coverage-7.10.6-cp310-cp310-win32.whl", hash = "sha256:86b9b59f2b16e981906e9d6383eb6446d5b46c278460ae2c36487667717eccf1"},
{file = "coverage-7.10.6-cp310-cp310-win_amd64.whl", hash = "sha256:e132b9152749bd33534e5bd8565c7576f135f157b4029b975e15ee184325f528"},
{file = "coverage-7.10.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c706db3cabb7ceef779de68270150665e710b46d56372455cd741184f3868d8f"},
{file = "coverage-7.10.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e0c38dc289e0508ef68ec95834cb5d2e96fdbe792eaccaa1bccac3966bbadcc"},
{file = "coverage-7.10.6-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:752a3005a1ded28f2f3a6e8787e24f28d6abe176ca64677bcd8d53d6fe2ec08a"},
{file = "coverage-7.10.6-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:689920ecfd60f992cafca4f5477d55720466ad2c7fa29bb56ac8d44a1ac2b47a"},
{file = "coverage-7.10.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec98435796d2624d6905820a42f82149ee9fc4f2d45c2c5bc5a44481cc50db62"},
{file = "coverage-7.10.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b37201ce4a458c7a758ecc4efa92fa8ed783c66e0fa3c42ae19fc454a0792153"},
{file = "coverage-7.10.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2904271c80898663c810a6b067920a61dd8d38341244a3605bd31ab55250dad5"},
{file = "coverage-7.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5aea98383463d6e1fa4e95416d8de66f2d0cb588774ee20ae1b28df826bcb619"},
{file = "coverage-7.10.6-cp311-cp311-win32.whl", hash = "sha256:e3fb1fa01d3598002777dd259c0c2e6d9d5e10e7222976fc8e03992f972a2cba"},
{file = "coverage-7.10.6-cp311-cp311-win_amd64.whl", hash = "sha256:f35ed9d945bece26553d5b4c8630453169672bea0050a564456eb88bdffd927e"},
{file = "coverage-7.10.6-cp311-cp311-win_arm64.whl", hash = "sha256:99e1a305c7765631d74b98bf7dbf54eeea931f975e80f115437d23848ee8c27c"},
{file = "coverage-7.10.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5b2dd6059938063a2c9fee1af729d4f2af28fd1a545e9b7652861f0d752ebcea"},
{file = "coverage-7.10.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:388d80e56191bf846c485c14ae2bc8898aa3124d9d35903fef7d907780477634"},
{file = "coverage-7.10.6-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:90cb5b1a4670662719591aa92d0095bb41714970c0b065b02a2610172dbf0af6"},
{file = "coverage-7.10.6-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:961834e2f2b863a0e14260a9a273aff07ff7818ab6e66d2addf5628590c628f9"},
{file = "coverage-7.10.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf9a19f5012dab774628491659646335b1928cfc931bf8d97b0d5918dd58033c"},
{file = "coverage-7.10.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:99c4283e2a0e147b9c9cc6bc9c96124de9419d6044837e9799763a0e29a7321a"},
{file = "coverage-7.10.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:282b1b20f45df57cc508c1e033403f02283adfb67d4c9c35a90281d81e5c52c5"},
{file = "coverage-7.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cdbe264f11afd69841bd8c0d83ca10b5b32853263ee62e6ac6a0ab63895f972"},
{file = "coverage-7.10.6-cp312-cp312-win32.whl", hash = "sha256:a517feaf3a0a3eca1ee985d8373135cfdedfbba3882a5eab4362bda7c7cf518d"},
{file = "coverage-7.10.6-cp312-cp312-win_amd64.whl", hash = "sha256:856986eadf41f52b214176d894a7de05331117f6035a28ac0016c0f63d887629"},
{file = "coverage-7.10.6-cp312-cp312-win_arm64.whl", hash = "sha256:acf36b8268785aad739443fa2780c16260ee3fa09d12b3a70f772ef100939d80"},
{file = "coverage-7.10.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ffea0575345e9ee0144dfe5701aa17f3ba546f8c3bb48db62ae101afb740e7d6"},
{file = "coverage-7.10.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:95d91d7317cde40a1c249d6b7382750b7e6d86fad9d8eaf4fa3f8f44cf171e80"},
{file = "coverage-7.10.6-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3e23dd5408fe71a356b41baa82892772a4cefcf758f2ca3383d2aa39e1b7a003"},
{file = "coverage-7.10.6-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0f3f56e4cb573755e96a16501a98bf211f100463d70275759e73f3cbc00d4f27"},
{file = "coverage-7.10.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db4a1d897bbbe7339946ffa2fe60c10cc81c43fab8b062d3fcb84188688174a4"},
{file = "coverage-7.10.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fd7879082953c156d5b13c74aa6cca37f6a6f4747b39538504c3f9c63d043d"},
{file = "coverage-7.10.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:28395ca3f71cd103b8c116333fa9db867f3a3e1ad6a084aa3725ae002b6583bc"},
{file = "coverage-7.10.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:61c950fc33d29c91b9e18540e1aed7d9f6787cc870a3e4032493bbbe641d12fc"},
{file = "coverage-7.10.6-cp313-cp313-win32.whl", hash = "sha256:160c00a5e6b6bdf4e5984b0ef21fc860bc94416c41b7df4d63f536d17c38902e"},
{file = "coverage-7.10.6-cp313-cp313-win_amd64.whl", hash = "sha256:628055297f3e2aa181464c3808402887643405573eb3d9de060d81531fa79d32"},
{file = "coverage-7.10.6-cp313-cp313-win_arm64.whl", hash = "sha256:df4ec1f8540b0bcbe26ca7dd0f541847cc8a108b35596f9f91f59f0c060bfdd2"},
{file = "coverage-7.10.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c9a8b7a34a4de3ed987f636f71881cd3b8339f61118b1aa311fbda12741bff0b"},
{file = "coverage-7.10.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8dd5af36092430c2b075cee966719898f2ae87b636cefb85a653f1d0ba5d5393"},
{file = "coverage-7.10.6-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b0353b0f0850d49ada66fdd7d0c7cdb0f86b900bb9e367024fd14a60cecc1e27"},
{file = "coverage-7.10.6-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d6b9ae13d5d3e8aeca9ca94198aa7b3ebbc5acfada557d724f2a1f03d2c0b0df"},
{file = "coverage-7.10.6-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:675824a363cc05781b1527b39dc2587b8984965834a748177ee3c37b64ffeafb"},
{file = "coverage-7.10.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:692d70ea725f471a547c305f0d0fc6a73480c62fb0da726370c088ab21aed282"},
{file = "coverage-7.10.6-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:851430a9a361c7a8484a36126d1d0ff8d529d97385eacc8dfdc9bfc8c2d2cbe4"},
{file = "coverage-7.10.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d9369a23186d189b2fc95cc08b8160ba242057e887d766864f7adf3c46b2df21"},
{file = "coverage-7.10.6-cp313-cp313t-win32.whl", hash = "sha256:92be86fcb125e9bda0da7806afd29a3fd33fdf58fba5d60318399adf40bf37d0"},
{file = "coverage-7.10.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6b3039e2ca459a70c79523d39347d83b73f2f06af5624905eba7ec34d64d80b5"},
{file = "coverage-7.10.6-cp313-cp313t-win_arm64.whl", hash = "sha256:3fb99d0786fe17b228eab663d16bee2288e8724d26a199c29325aac4b0319b9b"},
{file = "coverage-7.10.6-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6008a021907be8c4c02f37cdc3ffb258493bdebfeaf9a839f9e71dfdc47b018e"},
{file = "coverage-7.10.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5e75e37f23eb144e78940b40395b42f2321951206a4f50e23cfd6e8a198d3ceb"},
{file = "coverage-7.10.6-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0f7cb359a448e043c576f0da00aa8bfd796a01b06aa610ca453d4dde09cc1034"},
{file = "coverage-7.10.6-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c68018e4fc4e14b5668f1353b41ccf4bc83ba355f0e1b3836861c6f042d89ac1"},
{file = "coverage-7.10.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cd4b2b0707fc55afa160cd5fc33b27ccbf75ca11d81f4ec9863d5793fc6df56a"},
{file = "coverage-7.10.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4cec13817a651f8804a86e4f79d815b3b28472c910e099e4d5a0e8a3b6a1d4cb"},
{file = "coverage-7.10.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f2a6a8e06bbda06f78739f40bfb56c45d14eb8249d0f0ea6d4b3d48e1f7c695d"},
{file = "coverage-7.10.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:081b98395ced0d9bcf60ada7661a0b75f36b78b9d7e39ea0790bb4ed8da14747"},
{file = "coverage-7.10.6-cp314-cp314-win32.whl", hash = "sha256:6937347c5d7d069ee776b2bf4e1212f912a9f1f141a429c475e6089462fcecc5"},
{file = "coverage-7.10.6-cp314-cp314-win_amd64.whl", hash = "sha256:adec1d980fa07e60b6ef865f9e5410ba760e4e1d26f60f7e5772c73b9a5b0713"},
{file = "coverage-7.10.6-cp314-cp314-win_arm64.whl", hash = "sha256:a80f7aef9535442bdcf562e5a0d5a5538ce8abe6bb209cfbf170c462ac2c2a32"},
{file = "coverage-7.10.6-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:0de434f4fbbe5af4fa7989521c655c8c779afb61c53ab561b64dcee6149e4c65"},
{file = "coverage-7.10.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6e31b8155150c57e5ac43ccd289d079eb3f825187d7c66e755a055d2c85794c6"},
{file = "coverage-7.10.6-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:98cede73eb83c31e2118ae8d379c12e3e42736903a8afcca92a7218e1f2903b0"},
{file = "coverage-7.10.6-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f863c08f4ff6b64fa8045b1e3da480f5374779ef187f07b82e0538c68cb4ff8e"},
{file = "coverage-7.10.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b38261034fda87be356f2c3f42221fdb4171c3ce7658066ae449241485390d5"},
{file = "coverage-7.10.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e93b1476b79eae849dc3872faeb0bf7948fd9ea34869590bc16a2a00b9c82a7"},
{file = "coverage-7.10.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ff8a991f70f4c0cf53088abf1e3886edcc87d53004c7bb94e78650b4d3dac3b5"},
{file = "coverage-7.10.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ac765b026c9f33044419cbba1da913cfb82cca1b60598ac1c7a5ed6aac4621a0"},
{file = "coverage-7.10.6-cp314-cp314t-win32.whl", hash = "sha256:441c357d55f4936875636ef2cfb3bee36e466dcf50df9afbd398ce79dba1ebb7"},
{file = "coverage-7.10.6-cp314-cp314t-win_amd64.whl", hash = "sha256:073711de3181b2e204e4870ac83a7c4853115b42e9cd4d145f2231e12d670930"},
{file = "coverage-7.10.6-cp314-cp314t-win_arm64.whl", hash = "sha256:137921f2bac5559334ba66122b753db6dc5d1cf01eb7b64eb412bb0d064ef35b"},
{file = "coverage-7.10.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90558c35af64971d65fbd935c32010f9a2f52776103a259f1dee865fe8259352"},
{file = "coverage-7.10.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8953746d371e5695405806c46d705a3cd170b9cc2b9f93953ad838f6c1e58612"},
{file = "coverage-7.10.6-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c83f6afb480eae0313114297d29d7c295670a41c11b274e6bca0c64540c1ce7b"},
{file = "coverage-7.10.6-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7eb68d356ba0cc158ca535ce1381dbf2037fa8cb5b1ae5ddfc302e7317d04144"},
{file = "coverage-7.10.6-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b15a87265e96307482746d86995f4bff282f14b027db75469c446da6127433b"},
{file = "coverage-7.10.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fc53ba868875bfbb66ee447d64d6413c2db91fddcfca57025a0e7ab5b07d5862"},
{file = "coverage-7.10.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:efeda443000aa23f276f4df973cb82beca682fd800bb119d19e80504ffe53ec2"},
{file = "coverage-7.10.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9702b59d582ff1e184945d8b501ffdd08d2cee38d93a2206aa5f1365ce0b8d78"},
{file = "coverage-7.10.6-cp39-cp39-win32.whl", hash = "sha256:2195f8e16ba1a44651ca684db2ea2b2d4b5345da12f07d9c22a395202a05b23c"},
{file = "coverage-7.10.6-cp39-cp39-win_amd64.whl", hash = "sha256:f32ff80e7ef6a5b5b606ea69a36e97b219cd9dc799bcf2963018a4d8f788cfbf"},
{file = "coverage-7.10.6-py3-none-any.whl", hash = "sha256:92c4ecf6bf11b2e85fd4d8204814dc26e6a19f0c9d938c207c5cb0eadfcabbe3"},
{file = "coverage-7.10.6.tar.gz", hash = "sha256:f644a3ae5933a552a29dbb9aa2f90c677a875f80ebea028e5a52a4f429044b90"},
]
[package.dependencies]
tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}
[package.extras]
toml = ["tomli ; python_full_version <= \"3.11.0a6\""]
[[package]]
name = "cryptography"
version = "48.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-48.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:0c558d2cdffd8f4bbb30fc7134c74d2ca9a476f830bb053074498fbc86f41ed6"},
{file = "cryptography-48.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f5333311663ea94f75dd408665686aaf426563556bb5283554a3539177e03b8c"},
{file = "cryptography-48.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7995ef305d7165c3f11ae07f2517e5a4f1d5c18da1376a0a9ed496336b69e5f3"},
{file = "cryptography-48.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:40ba1f85eaa6959837b1d51c9767e230e14612eea4ef110ee8854ada22da1bf5"},
{file = "cryptography-48.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:369a6348999f94bbd53435c894377b20ab95f25a9065c283570e70150d8abc3c"},
{file = "cryptography-48.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a0e692c683f4df67815a2d258b324e66f4738bd7a96a218c826dce4f4bd05d8f"},
{file = "cryptography-48.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:18349bbc56f4743c8b12dc32e2bccb2cf83ee8b69a3bba74ef8ae857e26b3d25"},
{file = "cryptography-48.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e8eac43dfca5c4cccc6dad9a80504436fca53bb9bc3100a2386d730fbe6b602"},
{file = "cryptography-48.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9ccdac7d40688ecb5a3b4a604b8a88c8002e3442d6c60aead1db2a89a041560c"},
{file = "cryptography-48.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:bd72e68b06bb1e96913f97dd4901119bc17f39d4586a5adf2d3e47bc2b9d58b5"},
{file = "cryptography-48.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59baa2cb386c4f0b9905bd6eb4c2a79a69a128408fd31d32ca4d7102d4156321"},
{file = "cryptography-48.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9249e3cd978541d665967ac2cb2787fd6a62bddf1e75b3e347a594d7dacf4f74"},
{file = "cryptography-48.0.0-cp311-abi3-win32.whl", hash = "sha256:9c459db21422be75e2809370b829a87eb37f74cd785fc4aa9ea1e5f43b47cda4"},
{file = "cryptography-48.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:5b012212e08b8dd5edc78ef54da83dd9892fd9105323b3993eff6bea65dc21d7"},
{file = "cryptography-48.0.0-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:3cb07a3ed6431663cd321ea8a000a1314c74211f823e4177fefa2255e057d1ec"},
{file = "cryptography-48.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c7378637d7d88016fa6791c159f698b3d3eed28ebf844ac36b9dc04a14dae18"},
{file = "cryptography-48.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc90c0b39b2e3c65ef52c804b72e3c58f8a04ab2a1871272798e5f9572c17d20"},
{file = "cryptography-48.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:76341972e1eff8b4bea859f09c0d3e64b96ce931b084f9b9b7db8ef364c30eff"},
{file = "cryptography-48.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:55b7718303bf06a5753dcdccf2f3945cf18ad7bffde41b61226e4db31ab89a9c"},
{file = "cryptography-48.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:a64697c641c7b1b2178e573cbc31c7c6684cd56883a478d75143dbb7118036db"},
{file = "cryptography-48.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:561215ea3879cb1cbbf272867e2efda62476f240fb58c64de6b393ae19246741"},
{file = "cryptography-48.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ad64688338ed4bc1a6618076ba75fd7194a5f1797ac60b47afe926285adb3166"},
{file = "cryptography-48.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:906cbf0670286c6e0044156bc7d4af9cbb0ef6db9f73e52c3ec56ba6bdde5336"},
{file = "cryptography-48.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:ea8990436d914540a40ab24b6a77c0969695ed52f4a4874c5137ccf7045a7057"},
{file = "cryptography-48.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c18684a7f0cc9a3cb60328f496b8e3372def7c5d2df39ac267878b05565aaaae"},
{file = "cryptography-48.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9be5aafa5736574f8f15f262adc81b2a9869e2cfe9014d52a44633905b40d52c"},
{file = "cryptography-48.0.0-cp314-cp314t-win32.whl", hash = "sha256:c17dfe85494deaeddc5ce251aebd1d60bbe6afc8b62071bb0b469431a000124f"},
{file = "cryptography-48.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27241b1dc9962e056062a8eef1991d02c3a24569c95975bd2322a8a52c6e5e12"},
{file = "cryptography-48.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:58d00498e8933e4a194f3076aee1b4a97dfec1a6da444535755822fe5d8b0b86"},
{file = "cryptography-48.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:614d0949f4790582d2cc25553abd09dd723025f0c0e7c67376a1d77196743d6e"},
{file = "cryptography-48.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ce4bfae76319a532a2dc68f82cc32f5676ee792a983187dac07183690e5c66f"},
{file = "cryptography-48.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2eb992bbd4661238c5a397594c83f5b4dc2bc5b848c365c8f991b6780efcc5c7"},
{file = "cryptography-48.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:22a5cb272895dce158b2cacdfdc3debd299019659f42947dbdac6f32d68fe832"},
{file = "cryptography-48.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2b4d59804e8408e2fea7d1fbaf218e5ec984325221db76e6a241a9abd6cdd95c"},
{file = "cryptography-48.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:984a20b0f62a26f48a3396c72e4bc34c66e356d356bf370053066b3b6d54634a"},
{file = "cryptography-48.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5a5ed8fde7a1d09376ca0b40e68cd59c69fe23b1f9768bd5824f54681626032a"},
{file = "cryptography-48.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:8cd666227ef7af430aa5914a9910e0ddd703e75f039cef0825cd0da71b6b711a"},
{file = "cryptography-48.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9071196d81abc88b3516ac8cdfad32e2b66dd4a5393a8e68a961e9161ddc6239"},
{file = "cryptography-48.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e2d54c8be6152856a36f0882ab231e70f8ec7f14e93cf87db8a2ed056bf160c"},
{file = "cryptography-48.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a5da777e32ffed6f85a7b2b3f7c5cbc88c146bfcd0a1d7baf5fcc6c52ee35dd4"},
{file = "cryptography-48.0.0-cp39-abi3-win32.whl", hash = "sha256:77a2ccbbe917f6710e05ba9adaa25fb5075620bf3ea6fb751997875aff4ae4bd"},
{file = "cryptography-48.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:16cd65b9330583e4619939b3a3843eec1e6e789744bb01e7c7e2e62e33c239c8"},
{file = "cryptography-48.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:84cf79f0dc8b36ac5da873481716e87aef31fcfa0444f9e1d8b4b2cece142855"},
{file = "cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:fdfef35d751d510fcef5252703621574364fec16418c4a1e5e1055248401054b"},
{file = "cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:0890f502ddf7d9c6426129c3f49f5c0a39278ed7cd6322c8755ffca6ee675a13"},
{file = "cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:ecde28a596bead48b0cfd2a1b4416c3d43074c2d785e3a398d7ec1fc4d0f7fbb"},
{file = "cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:4defde8685ae324a9eb9d818717e93b4638ef67070ac9bc15b8ca85f63048355"},
{file = "cryptography-48.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:db63bf618e5dea46c07de12e900fe1cdd2541e6dc9dbae772a70b7d4d4765f6a"},
{file = "cryptography-48.0.0.tar.gz", hash = "sha256:5c3932f4436d1cccb036cb0eaef46e6e2db91035166f1ad6505c3c9d5a635920"},
]
[package.dependencies]
cffi = {version = ">=2.0.0", markers = "platform_python_implementation != \"PyPy\""}
typing-extensions = {version = ">=4.13.2", markers = "python_full_version < \"3.11.0\""}
[package.extras]
ssh = ["bcrypt (>=3.1.5)"]
[[package]]
name = "exceptiongroup"
version = "1.2.2"
description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
groups = ["dev"]
markers = "python_version == \"3.10\""
files = [
{file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
{file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
]
[package.extras]
test = ["pytest (>=6)"]
[[package]]
name = "home-assistant-bluetooth"
version = "1.10.4"
description = "Home Assistant Bluetooth Models and Helpers"
optional = false
python-versions = ">=3.9,<4.0"
groups = ["main"]
files = [
{file = "home_assistant_bluetooth-1.10.4-cp310-cp310-manylinux_2_31_x86_64.whl", hash = "sha256:7c3434bdec5dcfe733d3e7c56d4a24418fcd03718dc2e7707c9133d1e48145a8"},
{file = "home_assistant_bluetooth-1.10.4.tar.gz", hash = "sha256:21216b6be9d028bc232b9188ac4dce773798c6b4e47482cc3524bfc5f82515e3"},
]
[[package]]
name = "iniconfig"
version = "2.0.0"
description = "brain-dead simple config-ini parsing"
optional = false
python-versions = ">=3.7"
groups = ["dev"]
files = [
{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
]
[[package]]
name = "packaging"
version = "24.1"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
{file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
]
[[package]]
name = "pluggy"
version = "1.5.0"
description = "plugin and hook calling mechanisms for python"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
{file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
]
[package.extras]
dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "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 = "pygments"
version = "2.20.0"
description = "Pygments is a syntax highlighting package written in Python."
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176"},
{file = "pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f"},
]
[package.extras]
windows-terminal = ["colorama (>=0.4.6)"]
[[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\""}
exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""}
iniconfig = ">=1.0.1"
packaging = ">=22"
pluggy = ">=1.5,<2"
pygments = ">=2.7.2"
tomli = {version = ">=1", markers = "python_version < \"3.11\""}
[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 = "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 = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
optional = false
python-versions = ">=3.7"
groups = ["dev"]
markers = "python_full_version <= \"3.11.0a6\""
files = [
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
]
[[package]]
name = "typing-extensions"
version = "4.15.0"
description = "Backported and Experimental Type Hints for Python 3.9+"
optional = false
python-versions = ">=3.9"
groups = ["main"]
markers = "python_version == \"3.10\""
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"},
]
[metadata]
lock-version = "2.1"
python-versions = "^3.10"
content-hash = "7fd7c96440cbca10961c3875ce1b8e287b69f09f16b215b21c085a791b847ec8"
mopeka-iot-ble-1.0.0/pyproject.toml 0000664 0000000 0000000 00000004034 15235413126 0017234 0 ustar 00root root 0000000 0000000 [tool.poetry]
name = "mopeka-iot-ble"
version = "1.0.0"
description = "Mopeka IOT BLE Parser"
authors = ["J. Nick Koston "]
license = "MIT"
readme = "README.md"
repository = "https://github.com/bluetooth-devices/mopeka-iot-ble"
classifiers = [
"Development Status :: 2 - Pre-Alpha",
"Intended Audience :: Developers",
"Natural Language :: English",
"Operating System :: OS Independent",
"Topic :: Software Development :: Libraries",
]
packages = [
{ include = "mopeka_iot_ble", from = "src" },
]
[tool.poetry.urls]
"Bug Tracker" = "https://github.com/bluetooth-devices/mopeka-iot-ble/issues"
"Changelog" = "https://github.com/bluetooth-devices/mopeka-iot-ble/blob/main/CHANGELOG.md"
[tool.poetry.dependencies]
python = "^3.10"
home-assistant-bluetooth = ">=1.3.0"
sensor-state-data = ">=2.2.0"
bluetooth-sensor-state-data = ">=1.5.0"
bluetooth-data-tools = ">=0.1.2"
[tool.poetry.group.dev.dependencies]
pytest = ">=7,<10"
pytest-cov = ">=3,<8"
[tool.semantic_release]
branch = "main"
version_toml = ["pyproject.toml:tool.poetry.version"]
version_variables = ["src/mopeka_iot_ble/__init__.py:__version__"]
build_command = "pip install poetry && poetry build"
[tool.pytest.ini_options]
addopts = "-v -Wdefault --cov=mopeka_iot_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",
'if __name__ == "__main__":',
]
[tool.isort]
profile = "black"
known_first_party = ["mopeka_iot_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 = [
'setup.py',
]
[[tool.mypy.overrides]]
module = "tests.*"
allow_untyped_defs = true
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
mopeka-iot-ble-1.0.0/renovate.json 0000664 0000000 0000000 00000000101 15235413126 0017025 0 ustar 00root root 0000000 0000000 {
"extends": ["github>browniebroke/renovate-configs:python"]
}
mopeka-iot-ble-1.0.0/setup.py 0000664 0000000 0000000 00000000335 15235413126 0016032 0 ustar 00root root 0000000 0000000 # 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="mopeka-iot-ble")
mopeka-iot-ble-1.0.0/src/ 0000775 0000000 0000000 00000000000 15235413126 0015106 5 ustar 00root root 0000000 0000000 mopeka-iot-ble-1.0.0/src/mopeka_iot_ble/ 0000775 0000000 0000000 00000000000 15235413126 0020057 5 ustar 00root root 0000000 0000000 mopeka-iot-ble-1.0.0/src/mopeka_iot_ble/__init__.py 0000664 0000000 0000000 00000001565 15235413126 0022177 0 ustar 00root root 0000000 0000000 """Parser for Mopeka IOT BLE advertisements.
Thanks to https://github.com/spbrogan/mopeka_pro_check for
help decoding the advertisements.
MIT License applies.
"""
from __future__ import annotations
from sensor_state_data import (
BinarySensorDescription,
BinarySensorDeviceClass,
BinarySensorValue,
DeviceClass,
DeviceKey,
SensorDescription,
SensorDeviceClass,
SensorDeviceInfo,
SensorUpdate,
SensorValue,
Units,
)
from .models import MediumType
from .parser import MopekaIOTBluetoothDeviceData
__version__ = "1.0.0"
__all__ = [
"BinarySensorDescription",
"BinarySensorDeviceClass",
"BinarySensorValue",
"DeviceClass",
"DeviceKey",
"MediumType",
"MopekaIOTBluetoothDeviceData",
"SensorDescription",
"SensorDeviceClass",
"SensorDeviceInfo",
"SensorUpdate",
"SensorValue",
"Units",
]
mopeka-iot-ble-1.0.0/src/mopeka_iot_ble/models.py 0000664 0000000 0000000 00000001116 15235413126 0021713 0 ustar 00root root 0000000 0000000 """Types for Mopeka IOT BLE advertisements.
Thanks to https://github.com/spbrogan/mopeka_pro_check for
help decoding the advertisements.
MIT License applies.
"""
from enum import Enum
class MediumType(Enum):
"""Enumeration of medium types for tank level measurements."""
PROPANE = "propane"
AIR = "air"
FRESH_WATER = "fresh_water"
WASTE_WATER = "waste_water"
LIVE_WELL = "live_well"
BLACK_WATER = "black_water"
RAW_WATER = "raw_water"
GASOLINE = "gasoline"
DIESEL = "diesel"
LNG = "lng"
OIL = "oil"
HYDRAULIC_OIL = "hydraulic_oil"
mopeka-iot-ble-1.0.0/src/mopeka_iot_ble/parser.py 0000664 0000000 0000000 00000015247 15235413126 0021736 0 ustar 00root root 0000000 0000000 """
Parser for Gmopeka_iot BLE advertisements.
Thanks to https://github.com/spbrogan/mopeka_pro_check for
help decoding the advertisements.
MIT License applies.
"""
from __future__ import annotations
import logging
from dataclasses import dataclass
from bluetooth_data_tools import short_address
from bluetooth_sensor_state_data import BluetoothData
from home_assistant_bluetooth import BluetoothServiceInfo
from sensor_state_data import (
BinarySensorDeviceClass,
SensorDeviceClass,
SensorLibrary,
Units,
)
from .models import MediumType
_LOGGER = logging.getLogger(__name__)
# converting sensor value to height
MOPEKA_TANK_LEVEL_COEFFICIENTS = {
MediumType.PROPANE: (0.573045, -0.002822, -0.00000535),
MediumType.AIR: (0.153096, 0.000327, -0.000000294),
MediumType.FRESH_WATER: (0.600592, 0.003124, -0.00001368),
MediumType.WASTE_WATER: (0.600592, 0.003124, -0.00001368),
MediumType.LIVE_WELL: (0.600592, 0.003124, -0.00001368),
MediumType.BLACK_WATER: (0.600592, 0.003124, -0.00001368),
MediumType.RAW_WATER: (0.600592, 0.003124, -0.00001368),
MediumType.GASOLINE: (0.7373417462, -0.001978229885, 0.00000202162),
MediumType.DIESEL: (0.7373417462, -0.001978229885, 0.00000202162),
MediumType.LNG: (0.7373417462, -0.001978229885, 0.00000202162),
MediumType.OIL: (0.7373417462, -0.001978229885, 0.00000202162),
MediumType.HYDRAULIC_OIL: (0.7373417462, -0.001978229885, 0.00000202162),
}
MOPEKA_MANUFACTURER = 89
MOKPEKA_PRO_SERVICE_UUID = "0000fee5-0000-1000-8000-00805f9b34fb"
@dataclass
class MopekaDevice:
model: str
name: str
adv_length: int
DEVICE_TYPES = {
0x3: MopekaDevice("M1017", "Pro Check", 10),
0x4: MopekaDevice("Pro-200", "Pro-200", 10),
0x5: MopekaDevice("Pro H20", "Pro Check H2O", 10),
0x6: MopekaDevice("M1017", "Lippert BottleCheck", 10),
0x8: MopekaDevice("M1015", "Pro Plus", 10),
0x9: MopekaDevice("M1015", "Pro Plus with Cellular", 10),
0xA: MopekaDevice("TD40/TD200", "TD40/TD200", 10),
0xB: MopekaDevice("TD40/TD200", "TD40/TD200 with Cellular", 10),
0xC: MopekaDevice("M1017", "Pro Check Universal", 10),
0x12: MopekaDevice("Pro-200", "Pro-200B", 10),
}
def hex(data: bytes) -> str:
"""Return a string object containing two hexadecimal digits for each byte in the instance."""
return "b'{}'".format("".join(f"\\x{b:02x}" for b in data))
def battery_to_voltage(battery: int) -> float:
"""Convert battery value to voltage"""
return battery / 32.0
def battery_to_percentage(battery: int) -> float:
"""Convert battery value to percentage."""
return round(max(0, min(100, (((battery / 32.0) - 2.2) / 0.65) * 100)), 1)
def temp_to_celsius(temp: int) -> int:
"""Convert temperature value to celsius."""
return temp - 40
def tank_level_to_mm(tank_level: int) -> int:
"""Convert tank level value to mm."""
return tank_level * 10
def tank_level_and_temp_to_mm(
tank_level: int, temp: int, medium: MediumType = MediumType.PROPANE
) -> int:
"""Get the tank level in mm for a given fluid type."""
coefs = MOPEKA_TANK_LEVEL_COEFFICIENTS[medium]
return int(tank_level * (coefs[0] + (coefs[1] * temp) + (coefs[2] * (temp**2))))
class MopekaIOTBluetoothDeviceData(BluetoothData):
"""Data for Mopeka IOT BLE sensors."""
def __init__(self, medium_type: MediumType = MediumType.PROPANE) -> None:
super().__init__()
self._medium_type = medium_type
def _start_update(self, service_info: BluetoothServiceInfo) -> None:
"""Update from BLE advertisement data."""
_LOGGER.debug(
"Parsing Mopeka IOT BLE advertisement data: %s, MediumType is: %s",
service_info,
self._medium_type,
)
manufacturer_data = service_info.manufacturer_data
service_uuids = service_info.service_uuids
address = service_info.address
if (
MOPEKA_MANUFACTURER not in manufacturer_data
or MOKPEKA_PRO_SERVICE_UUID not in service_uuids
):
_LOGGER.debug("Not a Mopeka IOT BLE advertisement: %s", service_info)
return
data = manufacturer_data[MOPEKA_MANUFACTURER]
model_num = data[0]
if not (device_type := DEVICE_TYPES.get(model_num)):
_LOGGER.debug("Unsupported Mopeka IOT BLE advertisement: %s", service_info)
return
adv_length = device_type.adv_length
if len(data) != adv_length:
return
self.set_device_manufacturer("Mopeka IOT")
self.set_device_type(device_type.model)
self.set_device_name(f"{device_type.name} {short_address(address)}")
battery = data[1]
battery_voltage = battery_to_voltage(battery)
battery_percentage = battery_to_percentage(battery)
button_pressed = bool(data[2] & 0x80 > 0)
temp = data[2] & 0x7F
temp_celsius = temp_to_celsius(temp)
tank_level = ((int(data[4]) << 8) + data[3]) & 0x3FFF
tank_level_mm = tank_level_and_temp_to_mm(tank_level, temp, self._medium_type)
reading_quality = data[4] >> 6
accelerometer_x = data[8]
accelerometer_y = data[9]
self.update_predefined_sensor(SensorLibrary.TEMPERATURE__CELSIUS, temp_celsius)
self.update_predefined_sensor(
SensorLibrary.BATTERY__PERCENTAGE, battery_percentage
)
self.update_predefined_sensor(
SensorLibrary.VOLTAGE__ELECTRIC_POTENTIAL_VOLT,
battery_voltage,
name="Battery Voltage",
key="battery_voltage",
)
self.update_predefined_binary_sensor(
BinarySensorDeviceClass.OCCUPANCY,
button_pressed,
key="button_pressed",
name="Button pressed",
)
self.update_sensor(
"tank_level",
Units.LENGTH_MILLIMETERS,
tank_level_mm if reading_quality >= 1 else None,
SensorDeviceClass.DISTANCE,
"Tank Level",
)
self.update_sensor(
"accelerometer_x",
None,
accelerometer_x,
None,
"Position X",
)
self.update_sensor(
"accelerometer_y",
None,
accelerometer_y,
None,
"Position Y",
)
self.update_sensor(
"reading_quality_raw",
None,
reading_quality,
None,
"Reading quality raw",
)
self.update_sensor(
"reading_quality",
Units.PERCENTAGE,
round(reading_quality / 3 * 100),
None,
"Reading quality",
)
# Reading stars = (3-reading_quality) * "★" + (reading_quality * "⭐")
mopeka-iot-ble-1.0.0/src/mopeka_iot_ble/py.typed 0000664 0000000 0000000 00000000000 15235413126 0021544 0 ustar 00root root 0000000 0000000 mopeka-iot-ble-1.0.0/tests/ 0000775 0000000 0000000 00000000000 15235413126 0015461 5 ustar 00root root 0000000 0000000 mopeka-iot-ble-1.0.0/tests/__init__.py 0000664 0000000 0000000 00000000000 15235413126 0017560 0 ustar 00root root 0000000 0000000 mopeka-iot-ble-1.0.0/tests/test_parser.py 0000664 0000000 0000000 00000211461 15235413126 0020373 0 ustar 00root root 0000000 0000000 import pytest
from bluetooth_sensor_state_data import BluetoothServiceInfo, SensorUpdate
from sensor_state_data import (
BinarySensorDescription,
BinarySensorDeviceClass,
BinarySensorValue,
DeviceKey,
SensorDescription,
SensorDeviceClass,
SensorDeviceInfo,
SensorValue,
Units,
)
from mopeka_iot_ble import MediumType
# Consider renaming the hex method to avoid the override complaint
from mopeka_iot_ble.parser import (
MopekaIOTBluetoothDeviceData,
battery_to_percentage,
battery_to_voltage,
hex,
tank_level_and_temp_to_mm,
tank_level_to_mm,
temp_to_celsius,
)
PRO_SERVICE_BAD_QUALITY_INFO = BluetoothServiceInfo(
name="",
address="C9:F3:32:E0:F5:09",
rssi=-63,
manufacturer_data={89: b"\x08rF\x000\xe0\xf5\t\xf0\xd8"},
service_uuids=["0000fee5-0000-1000-8000-00805f9b34fb"],
service_data={},
source="local",
)
PRO_SERVICE_LOW_QUALITY_INFO = BluetoothServiceInfo(
name="",
address="C9:F3:32:E0:F5:09",
rssi=-63,
manufacturer_data={89: b"\x08rF\x00@\xe0\xf5\t\xf0\xd8"},
service_uuids=["0000fee5-0000-1000-8000-00805f9b34fb"],
service_data={},
source="local",
)
TDR40_AIR_LOW_QUALITY_INFO_2 = BluetoothServiceInfo(
name="",
address="DA:D8:AC:6A:75:10",
rssi=-49,
manufacturer_data={89: b"\x0c`8<\x83*\xea\x8c1\xf8"},
service_uuids=["0000fee5-0000-1000-8000-00805f9b34fb"],
service_data={},
source="local",
)
PRO_SERVICE_GOOD_QUALITY_INFO = BluetoothServiceInfo(
name="",
address="C9:F3:32:E0:F5:09",
rssi=-63,
manufacturer_data={89: b"\x08rF\x00\xc0\xe0\xf5\t\xf0\xd8"},
service_uuids=["0000fee5-0000-1000-8000-00805f9b34fb"],
service_data={},
source="local",
)
PRO_INSTALLED_SERVICE_INFO = BluetoothServiceInfo(
name="",
address="C9:F3:32:E0:F5:09",
rssi=-63,
manufacturer_data={89: b"\x08pC\xb6\xc3\xe0\xf5\t\xfa\xe3"},
service_uuids=["0000fee5-0000-1000-8000-00805f9b34fb"],
service_data={},
source="local",
)
LIPPERT_SERVICE_INFO = BluetoothServiceInfo(
name="",
address="C9:F3:32:E0:F5:09",
rssi=-63,
manufacturer_data={89: b"\x06pC\xb6\xc3\xe0\xf5\t\xfa\xe3"},
service_uuids=["0000fee5-0000-1000-8000-00805f9b34fb"],
service_data={},
source="local",
)
CHECK_INSTALLED_SERVICE_INFO = BluetoothServiceInfo(
name="",
address="C9:F3:32:E0:F5:09",
rssi=-63,
manufacturer_data={89: b"\x03pC\xb6\xc3\xe0\xf5\t\xfa\xe3"},
service_uuids=["0000fee5-0000-1000-8000-00805f9b34fb"],
service_data={},
source="local",
)
CHECK_UNIVERSAL_INSTALLED_SERVICE_INFO = BluetoothServiceInfo(
name="",
address="C9:F3:32:E0:F5:09",
rssi=-63,
manufacturer_data={89: b"\x0cpC\xb6\xc3\xe0\xf5\t\xfa\xe3"},
service_uuids=["0000fee5-0000-1000-8000-00805f9b34fb"],
service_data={},
source="local",
)
PRO_200B_SERVICE_INFO = BluetoothServiceInfo(
name="",
address="CB:A1:6C:CC:E5:2B",
rssi=-63,
manufacturer_data={89: b"\x12\x5f\x31\x78\xc7\xcc\xe5\x2b\xa8\x3f"},
service_uuids=["0000fee5-0000-1000-8000-00805f9b34fb"],
service_data={},
source="local",
)
TDR40_AIR_BAD_QUALITY_INFO = BluetoothServiceInfo(
name="",
address="DA:D8:AC:6A:75:10",
rssi=-60,
manufacturer_data={89: b"\ns@NMju\x10\x7f\x80"},
service_uuids=["0000fee5-0000-1000-8000-00805f9b34fb"],
service_data={},
source="local",
)
TDR40_AIR_LOW_QUALITY_INFO = BluetoothServiceInfo(
name="",
address="DA:D8:AC:6A:75:10",
rssi=-44,
manufacturer_data={89: b"\x0c`8<\x83*\xea\x8c1\xf8"},
service_uuids=["0000fee5-0000-1000-8000-00805f9b34fb"],
service_data={},
source="local",
)
TDR40_AIR_GOOD_QUALITY_INFO = BluetoothServiceInfo(
name="",
address="DA:D8:AC:6A:75:10",
rssi=-50,
manufacturer_data={89: b"\nq@}\xd0ju\x10\x80 "},
service_uuids=["0000fee5-0000-1000-8000-00805f9b34fb"],
service_data={},
source="local",
)
def test_hex():
assert (
hex(b"\x08rF\x000\xe0\xf5\t\xf0\xd8")
== "b'\\x08\\x72\\x46\\x00\\x30\\xe0\\xf5\\x09\\xf0\\xd8'"
)
def test_can_create():
MopekaIOTBluetoothDeviceData()
def test_pro_bad_quality():
parser = MopekaIOTBluetoothDeviceData()
service_info = PRO_SERVICE_BAD_QUALITY_INFO
result = parser.update(service_info)
assert result == SensorUpdate(
title=None,
devices={
None: SensorDeviceInfo(
name="Pro Plus F509",
model="M1015",
manufacturer="Mopeka IOT",
sw_version=None,
hw_version=None,
)
},
entity_descriptions={
DeviceKey(key="battery_voltage", device_id=None): SensorDescription(
device_key=DeviceKey(key="battery_voltage", device_id=None),
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=Units.ELECTRIC_POTENTIAL_VOLT,
),
DeviceKey(key="tank_level", device_id=None): SensorDescription(
device_key=DeviceKey(key="tank_level", device_id=None),
device_class=SensorDeviceClass.DISTANCE,
native_unit_of_measurement=Units.LENGTH_MILLIMETERS,
),
DeviceKey(key="temperature", device_id=None): SensorDescription(
device_key=DeviceKey(key="temperature", device_id=None),
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=Units.TEMP_CELSIUS,
),
DeviceKey(key="battery", device_id=None): SensorDescription(
device_key=DeviceKey(key="battery", device_id=None),
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=Units.PERCENTAGE,
),
DeviceKey(key="signal_strength", device_id=None): SensorDescription(
device_key=DeviceKey(key="signal_strength", device_id=None),
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
),
DeviceKey(key="reading_quality", device_id=None): SensorDescription(
device_key=DeviceKey(key="reading_quality", device_id=None),
device_class=None,
native_unit_of_measurement=Units.PERCENTAGE,
),
DeviceKey(key="reading_quality_raw", device_id=None): SensorDescription(
device_key=DeviceKey(key="reading_quality_raw", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
DeviceKey(key="accelerometer_y", device_id=None): SensorDescription(
device_key=DeviceKey(key="accelerometer_y", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
DeviceKey(key="accelerometer_x", device_id=None): SensorDescription(
device_key=DeviceKey(key="accelerometer_x", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
},
entity_values={
DeviceKey(key="battery_voltage", device_id=None): SensorValue(
device_key=DeviceKey(key="battery_voltage", device_id=None),
name="Battery Voltage",
native_value=3.5625,
),
DeviceKey(key="tank_level", device_id=None): SensorValue(
device_key=DeviceKey(key="tank_level", device_id=None),
name="Tank Level",
native_value=None,
),
DeviceKey(key="temperature", device_id=None): SensorValue(
device_key=DeviceKey(key="temperature", device_id=None),
name="Temperature",
native_value=30,
),
DeviceKey(key="battery", device_id=None): SensorValue(
device_key=DeviceKey(key="battery", device_id=None),
name="Battery",
native_value=100,
),
DeviceKey(key="signal_strength", device_id=None): SensorValue(
device_key=DeviceKey(key="signal_strength", device_id=None),
name="Signal Strength",
native_value=-63,
),
DeviceKey(key="reading_quality", device_id=None): SensorValue(
device_key=DeviceKey(key="reading_quality", device_id=None),
name="Reading quality",
native_value=0,
),
DeviceKey(key="reading_quality_raw", device_id=None): SensorValue(
device_key=DeviceKey(key="reading_quality_raw", device_id=None),
name="Reading quality raw",
native_value=0,
),
DeviceKey(key="accelerometer_y", device_id=None): SensorValue(
device_key=DeviceKey(key="accelerometer_y", device_id=None),
name="Position Y",
native_value=216,
),
DeviceKey(key="accelerometer_x", device_id=None): SensorValue(
device_key=DeviceKey(key="accelerometer_x", device_id=None),
name="Position X",
native_value=240,
),
},
binary_entity_descriptions={
DeviceKey(key="button_pressed", device_id=None): BinarySensorDescription(
device_key=DeviceKey(key="button_pressed", device_id=None),
device_class=BinarySensorDeviceClass.OCCUPANCY,
)
},
binary_entity_values={
DeviceKey(key="button_pressed", device_id=None): BinarySensorValue(
device_key=DeviceKey(key="button_pressed", device_id=None),
name="Button pressed",
native_value=False,
)
},
events={},
)
def test_pro_low_quality():
parser = MopekaIOTBluetoothDeviceData()
service_info = PRO_SERVICE_LOW_QUALITY_INFO
result = parser.update(service_info)
assert result == SensorUpdate(
title=None,
devices={
None: SensorDeviceInfo(
name="Pro Plus F509",
model="M1015",
manufacturer="Mopeka IOT",
sw_version=None,
hw_version=None,
)
},
entity_descriptions={
DeviceKey(key="battery_voltage", device_id=None): SensorDescription(
device_key=DeviceKey(key="battery_voltage", device_id=None),
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=Units.ELECTRIC_POTENTIAL_VOLT,
),
DeviceKey(key="tank_level", device_id=None): SensorDescription(
device_key=DeviceKey(key="tank_level", device_id=None),
device_class=SensorDeviceClass.DISTANCE,
native_unit_of_measurement=Units.LENGTH_MILLIMETERS,
),
DeviceKey(key="temperature", device_id=None): SensorDescription(
device_key=DeviceKey(key="temperature", device_id=None),
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=Units.TEMP_CELSIUS,
),
DeviceKey(key="battery", device_id=None): SensorDescription(
device_key=DeviceKey(key="battery", device_id=None),
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=Units.PERCENTAGE,
),
DeviceKey(key="signal_strength", device_id=None): SensorDescription(
device_key=DeviceKey(key="signal_strength", device_id=None),
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
),
DeviceKey(key="reading_quality", device_id=None): SensorDescription(
device_key=DeviceKey(key="reading_quality", device_id=None),
device_class=None,
native_unit_of_measurement=Units.PERCENTAGE,
),
DeviceKey(key="reading_quality_raw", device_id=None): SensorDescription(
device_key=DeviceKey(key="reading_quality_raw", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
DeviceKey(key="accelerometer_y", device_id=None): SensorDescription(
device_key=DeviceKey(key="accelerometer_y", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
DeviceKey(key="accelerometer_x", device_id=None): SensorDescription(
device_key=DeviceKey(key="accelerometer_x", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
},
entity_values={
DeviceKey(key="battery_voltage", device_id=None): SensorValue(
device_key=DeviceKey(key="battery_voltage", device_id=None),
name="Battery Voltage",
native_value=3.5625,
),
DeviceKey(key="tank_level", device_id=None): SensorValue(
device_key=DeviceKey(key="tank_level", device_id=None),
name="Tank Level",
native_value=0,
),
DeviceKey(key="temperature", device_id=None): SensorValue(
device_key=DeviceKey(key="temperature", device_id=None),
name="Temperature",
native_value=30,
),
DeviceKey(key="battery", device_id=None): SensorValue(
device_key=DeviceKey(key="battery", device_id=None),
name="Battery",
native_value=100,
),
DeviceKey(key="signal_strength", device_id=None): SensorValue(
device_key=DeviceKey(key="signal_strength", device_id=None),
name="Signal Strength",
native_value=-63,
),
DeviceKey(key="reading_quality", device_id=None): SensorValue(
device_key=DeviceKey(key="reading_quality", device_id=None),
name="Reading quality",
native_value=33,
),
DeviceKey(key="reading_quality_raw", device_id=None): SensorValue(
device_key=DeviceKey(key="reading_quality_raw", device_id=None),
name="Reading quality raw",
native_value=1,
),
DeviceKey(key="accelerometer_y", device_id=None): SensorValue(
device_key=DeviceKey(key="accelerometer_y", device_id=None),
name="Position Y",
native_value=216,
),
DeviceKey(key="accelerometer_x", device_id=None): SensorValue(
device_key=DeviceKey(key="accelerometer_x", device_id=None),
name="Position X",
native_value=240,
),
},
binary_entity_descriptions={
DeviceKey(key="button_pressed", device_id=None): BinarySensorDescription(
device_key=DeviceKey(key="button_pressed", device_id=None),
device_class=BinarySensorDeviceClass.OCCUPANCY,
)
},
binary_entity_values={
DeviceKey(key="button_pressed", device_id=None): BinarySensorValue(
device_key=DeviceKey(key="button_pressed", device_id=None),
name="Button pressed",
native_value=False,
)
},
events={},
)
def test_pro_good_quality():
parser = MopekaIOTBluetoothDeviceData()
service_info = PRO_SERVICE_GOOD_QUALITY_INFO
result = parser.update(service_info)
assert result == SensorUpdate(
title=None,
devices={
None: SensorDeviceInfo(
name="Pro Plus F509",
model="M1015",
manufacturer="Mopeka IOT",
sw_version=None,
hw_version=None,
)
},
entity_descriptions={
DeviceKey(key="battery_voltage", device_id=None): SensorDescription(
device_key=DeviceKey(key="battery_voltage", device_id=None),
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=Units.ELECTRIC_POTENTIAL_VOLT,
),
DeviceKey(key="tank_level", device_id=None): SensorDescription(
device_key=DeviceKey(key="tank_level", device_id=None),
device_class=SensorDeviceClass.DISTANCE,
native_unit_of_measurement=Units.LENGTH_MILLIMETERS,
),
DeviceKey(key="temperature", device_id=None): SensorDescription(
device_key=DeviceKey(key="temperature", device_id=None),
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=Units.TEMP_CELSIUS,
),
DeviceKey(key="battery", device_id=None): SensorDescription(
device_key=DeviceKey(key="battery", device_id=None),
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=Units.PERCENTAGE,
),
DeviceKey(key="signal_strength", device_id=None): SensorDescription(
device_key=DeviceKey(key="signal_strength", device_id=None),
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
),
DeviceKey(key="reading_quality", device_id=None): SensorDescription(
device_key=DeviceKey(key="reading_quality", device_id=None),
device_class=None,
native_unit_of_measurement=Units.PERCENTAGE,
),
DeviceKey(key="reading_quality_raw", device_id=None): SensorDescription(
device_key=DeviceKey(key="reading_quality_raw", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
DeviceKey(key="accelerometer_y", device_id=None): SensorDescription(
device_key=DeviceKey(key="accelerometer_y", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
DeviceKey(key="accelerometer_x", device_id=None): SensorDescription(
device_key=DeviceKey(key="accelerometer_x", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
},
entity_values={
DeviceKey(key="battery_voltage", device_id=None): SensorValue(
device_key=DeviceKey(key="battery_voltage", device_id=None),
name="Battery Voltage",
native_value=3.5625,
),
DeviceKey(key="tank_level", device_id=None): SensorValue(
device_key=DeviceKey(key="tank_level", device_id=None),
name="Tank Level",
native_value=0,
),
DeviceKey(key="temperature", device_id=None): SensorValue(
device_key=DeviceKey(key="temperature", device_id=None),
name="Temperature",
native_value=30,
),
DeviceKey(key="battery", device_id=None): SensorValue(
device_key=DeviceKey(key="battery", device_id=None),
name="Battery",
native_value=100,
),
DeviceKey(key="signal_strength", device_id=None): SensorValue(
device_key=DeviceKey(key="signal_strength", device_id=None),
name="Signal Strength",
native_value=-63,
),
DeviceKey(key="reading_quality", device_id=None): SensorValue(
device_key=DeviceKey(key="reading_quality", device_id=None),
name="Reading quality",
native_value=100,
),
DeviceKey(key="reading_quality_raw", device_id=None): SensorValue(
device_key=DeviceKey(key="reading_quality_raw", device_id=None),
name="Reading quality raw",
native_value=3,
),
DeviceKey(key="accelerometer_y", device_id=None): SensorValue(
device_key=DeviceKey(key="accelerometer_y", device_id=None),
name="Position Y",
native_value=216,
),
DeviceKey(key="accelerometer_x", device_id=None): SensorValue(
device_key=DeviceKey(key="accelerometer_x", device_id=None),
name="Position X",
native_value=240,
),
},
binary_entity_descriptions={
DeviceKey(key="button_pressed", device_id=None): BinarySensorDescription(
device_key=DeviceKey(key="button_pressed", device_id=None),
device_class=BinarySensorDeviceClass.OCCUPANCY,
)
},
binary_entity_values={
DeviceKey(key="button_pressed", device_id=None): BinarySensorValue(
device_key=DeviceKey(key="button_pressed", device_id=None),
name="Button pressed",
native_value=False,
)
},
events={},
)
def test_pro_installed():
parser = MopekaIOTBluetoothDeviceData()
service_info = PRO_INSTALLED_SERVICE_INFO
result = parser.update(service_info)
assert result == SensorUpdate(
title=None,
devices={
None: SensorDeviceInfo(
name="Pro Plus F509",
model="M1015",
manufacturer="Mopeka IOT",
sw_version=None,
hw_version=None,
)
},
entity_descriptions={
DeviceKey(key="battery_voltage", device_id=None): SensorDescription(
device_key=DeviceKey(key="battery_voltage", device_id=None),
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=Units.ELECTRIC_POTENTIAL_VOLT,
),
DeviceKey(key="tank_level", device_id=None): SensorDescription(
device_key=DeviceKey(key="tank_level", device_id=None),
device_class=SensorDeviceClass.DISTANCE,
native_unit_of_measurement=Units.LENGTH_MILLIMETERS,
),
DeviceKey(key="temperature", device_id=None): SensorDescription(
device_key=DeviceKey(key="temperature", device_id=None),
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=Units.TEMP_CELSIUS,
),
DeviceKey(key="battery", device_id=None): SensorDescription(
device_key=DeviceKey(key="battery", device_id=None),
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=Units.PERCENTAGE,
),
DeviceKey(key="signal_strength", device_id=None): SensorDescription(
device_key=DeviceKey(key="signal_strength", device_id=None),
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
),
DeviceKey(key="reading_quality", device_id=None): SensorDescription(
device_key=DeviceKey(key="reading_quality", device_id=None),
device_class=None,
native_unit_of_measurement=Units.PERCENTAGE,
),
DeviceKey(key="reading_quality_raw", device_id=None): SensorDescription(
device_key=DeviceKey(key="reading_quality_raw", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
DeviceKey(key="accelerometer_y", device_id=None): SensorDescription(
device_key=DeviceKey(key="accelerometer_y", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
DeviceKey(key="accelerometer_x", device_id=None): SensorDescription(
device_key=DeviceKey(key="accelerometer_x", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
},
entity_values={
DeviceKey(key="battery_voltage", device_id=None): SensorValue(
device_key=DeviceKey(key="battery_voltage", device_id=None),
name="Battery Voltage",
native_value=3.5,
),
DeviceKey(key="tank_level", device_id=None): SensorValue(
device_key=DeviceKey(key="tank_level", device_id=None),
name="Tank Level",
native_value=341,
),
DeviceKey(key="temperature", device_id=None): SensorValue(
device_key=DeviceKey(key="temperature", device_id=None),
name="Temperature",
native_value=27,
),
DeviceKey(key="battery", device_id=None): SensorValue(
device_key=DeviceKey(key="battery", device_id=None),
name="Battery",
native_value=100,
),
DeviceKey(key="signal_strength", device_id=None): SensorValue(
device_key=DeviceKey(key="signal_strength", device_id=None),
name="Signal Strength",
native_value=-63,
),
DeviceKey(key="reading_quality", device_id=None): SensorValue(
device_key=DeviceKey(key="reading_quality", device_id=None),
name="Reading quality",
native_value=100,
),
DeviceKey(key="reading_quality_raw", device_id=None): SensorValue(
device_key=DeviceKey(key="reading_quality_raw", device_id=None),
name="Reading quality raw",
native_value=3,
),
DeviceKey(key="accelerometer_y", device_id=None): SensorValue(
device_key=DeviceKey(key="accelerometer_y", device_id=None),
name="Position Y",
native_value=227,
),
DeviceKey(key="accelerometer_x", device_id=None): SensorValue(
device_key=DeviceKey(key="accelerometer_x", device_id=None),
name="Position X",
native_value=250,
),
},
binary_entity_descriptions={
DeviceKey(key="button_pressed", device_id=None): BinarySensorDescription(
device_key=DeviceKey(key="button_pressed", device_id=None),
device_class=BinarySensorDeviceClass.OCCUPANCY,
)
},
binary_entity_values={
DeviceKey(key="button_pressed", device_id=None): BinarySensorValue(
device_key=DeviceKey(key="button_pressed", device_id=None),
name="Button pressed",
native_value=False,
)
},
events={},
)
def test_check_universal_installed():
parser = MopekaIOTBluetoothDeviceData()
service_info = CHECK_UNIVERSAL_INSTALLED_SERVICE_INFO
result = parser.update(service_info)
assert result == SensorUpdate(
title=None,
devices={
None: SensorDeviceInfo(
name="Pro Check Universal F509",
model="M1017",
manufacturer="Mopeka IOT",
sw_version=None,
hw_version=None,
)
},
entity_descriptions={
DeviceKey(key="battery_voltage", device_id=None): SensorDescription(
device_key=DeviceKey(key="battery_voltage", device_id=None),
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=Units.ELECTRIC_POTENTIAL_VOLT,
),
DeviceKey(key="tank_level", device_id=None): SensorDescription(
device_key=DeviceKey(key="tank_level", device_id=None),
device_class=SensorDeviceClass.DISTANCE,
native_unit_of_measurement=Units.LENGTH_MILLIMETERS,
),
DeviceKey(key="temperature", device_id=None): SensorDescription(
device_key=DeviceKey(key="temperature", device_id=None),
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=Units.TEMP_CELSIUS,
),
DeviceKey(key="battery", device_id=None): SensorDescription(
device_key=DeviceKey(key="battery", device_id=None),
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=Units.PERCENTAGE,
),
DeviceKey(key="signal_strength", device_id=None): SensorDescription(
device_key=DeviceKey(key="signal_strength", device_id=None),
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
),
DeviceKey(key="reading_quality", device_id=None): SensorDescription(
device_key=DeviceKey(key="reading_quality", device_id=None),
device_class=None,
native_unit_of_measurement=Units.PERCENTAGE,
),
DeviceKey(key="reading_quality_raw", device_id=None): SensorDescription(
device_key=DeviceKey(key="reading_quality_raw", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
DeviceKey(key="accelerometer_y", device_id=None): SensorDescription(
device_key=DeviceKey(key="accelerometer_y", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
DeviceKey(key="accelerometer_x", device_id=None): SensorDescription(
device_key=DeviceKey(key="accelerometer_x", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
},
entity_values={
DeviceKey(key="battery_voltage", device_id=None): SensorValue(
device_key=DeviceKey(key="battery_voltage", device_id=None),
name="Battery Voltage",
native_value=3.5,
),
DeviceKey(key="tank_level", device_id=None): SensorValue(
device_key=DeviceKey(key="tank_level", device_id=None),
name="Tank Level",
native_value=341,
),
DeviceKey(key="temperature", device_id=None): SensorValue(
device_key=DeviceKey(key="temperature", device_id=None),
name="Temperature",
native_value=27,
),
DeviceKey(key="battery", device_id=None): SensorValue(
device_key=DeviceKey(key="battery", device_id=None),
name="Battery",
native_value=100,
),
DeviceKey(key="signal_strength", device_id=None): SensorValue(
device_key=DeviceKey(key="signal_strength", device_id=None),
name="Signal Strength",
native_value=-63,
),
DeviceKey(key="reading_quality", device_id=None): SensorValue(
device_key=DeviceKey(key="reading_quality", device_id=None),
name="Reading quality",
native_value=100,
),
DeviceKey(key="reading_quality_raw", device_id=None): SensorValue(
device_key=DeviceKey(key="reading_quality_raw", device_id=None),
name="Reading quality raw",
native_value=3,
),
DeviceKey(key="accelerometer_y", device_id=None): SensorValue(
device_key=DeviceKey(key="accelerometer_y", device_id=None),
name="Position Y",
native_value=227,
),
DeviceKey(key="accelerometer_x", device_id=None): SensorValue(
device_key=DeviceKey(key="accelerometer_x", device_id=None),
name="Position X",
native_value=250,
),
},
binary_entity_descriptions={
DeviceKey(key="button_pressed", device_id=None): BinarySensorDescription(
device_key=DeviceKey(key="button_pressed", device_id=None),
device_class=BinarySensorDeviceClass.OCCUPANCY,
)
},
binary_entity_values={
DeviceKey(key="button_pressed", device_id=None): BinarySensorValue(
device_key=DeviceKey(key="button_pressed", device_id=None),
name="Button pressed",
native_value=False,
)
},
events={},
)
def test_check_installed():
parser = MopekaIOTBluetoothDeviceData()
service_info = CHECK_INSTALLED_SERVICE_INFO
result = parser.update(service_info)
assert result == SensorUpdate(
title=None,
devices={
None: SensorDeviceInfo(
name="Pro Check F509",
model="M1017",
manufacturer="Mopeka IOT",
sw_version=None,
hw_version=None,
)
},
entity_descriptions={
DeviceKey(key="battery_voltage", device_id=None): SensorDescription(
device_key=DeviceKey(key="battery_voltage", device_id=None),
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=Units.ELECTRIC_POTENTIAL_VOLT,
),
DeviceKey(key="tank_level", device_id=None): SensorDescription(
device_key=DeviceKey(key="tank_level", device_id=None),
device_class=SensorDeviceClass.DISTANCE,
native_unit_of_measurement=Units.LENGTH_MILLIMETERS,
),
DeviceKey(key="temperature", device_id=None): SensorDescription(
device_key=DeviceKey(key="temperature", device_id=None),
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=Units.TEMP_CELSIUS,
),
DeviceKey(key="battery", device_id=None): SensorDescription(
device_key=DeviceKey(key="battery", device_id=None),
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=Units.PERCENTAGE,
),
DeviceKey(key="signal_strength", device_id=None): SensorDescription(
device_key=DeviceKey(key="signal_strength", device_id=None),
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
),
DeviceKey(key="reading_quality", device_id=None): SensorDescription(
device_key=DeviceKey(key="reading_quality", device_id=None),
device_class=None,
native_unit_of_measurement=Units.PERCENTAGE,
),
DeviceKey(key="reading_quality_raw", device_id=None): SensorDescription(
device_key=DeviceKey(key="reading_quality_raw", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
DeviceKey(key="accelerometer_y", device_id=None): SensorDescription(
device_key=DeviceKey(key="accelerometer_y", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
DeviceKey(key="accelerometer_x", device_id=None): SensorDescription(
device_key=DeviceKey(key="accelerometer_x", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
},
entity_values={
DeviceKey(key="battery_voltage", device_id=None): SensorValue(
device_key=DeviceKey(key="battery_voltage", device_id=None),
name="Battery Voltage",
native_value=3.5,
),
DeviceKey(key="tank_level", device_id=None): SensorValue(
device_key=DeviceKey(key="tank_level", device_id=None),
name="Tank Level",
native_value=341,
),
DeviceKey(key="temperature", device_id=None): SensorValue(
device_key=DeviceKey(key="temperature", device_id=None),
name="Temperature",
native_value=27,
),
DeviceKey(key="battery", device_id=None): SensorValue(
device_key=DeviceKey(key="battery", device_id=None),
name="Battery",
native_value=100,
),
DeviceKey(key="signal_strength", device_id=None): SensorValue(
device_key=DeviceKey(key="signal_strength", device_id=None),
name="Signal Strength",
native_value=-63,
),
DeviceKey(key="reading_quality", device_id=None): SensorValue(
device_key=DeviceKey(key="reading_quality", device_id=None),
name="Reading quality",
native_value=100,
),
DeviceKey(key="reading_quality_raw", device_id=None): SensorValue(
device_key=DeviceKey(key="reading_quality_raw", device_id=None),
name="Reading quality raw",
native_value=3,
),
DeviceKey(key="accelerometer_y", device_id=None): SensorValue(
device_key=DeviceKey(key="accelerometer_y", device_id=None),
name="Position Y",
native_value=227,
),
DeviceKey(key="accelerometer_x", device_id=None): SensorValue(
device_key=DeviceKey(key="accelerometer_x", device_id=None),
name="Position X",
native_value=250,
),
},
binary_entity_descriptions={
DeviceKey(key="button_pressed", device_id=None): BinarySensorDescription(
device_key=DeviceKey(key="button_pressed", device_id=None),
device_class=BinarySensorDeviceClass.OCCUPANCY,
)
},
binary_entity_values={
DeviceKey(key="button_pressed", device_id=None): BinarySensorValue(
device_key=DeviceKey(key="button_pressed", device_id=None),
name="Button pressed",
native_value=False,
)
},
events={},
)
def test_lippert():
parser = MopekaIOTBluetoothDeviceData()
service_info = LIPPERT_SERVICE_INFO
result = parser.update(service_info)
assert result == SensorUpdate(
title=None,
devices={
None: SensorDeviceInfo(
name="Lippert BottleCheck F509",
model="M1017",
manufacturer="Mopeka IOT",
sw_version=None,
hw_version=None,
)
},
entity_descriptions={
DeviceKey(key="battery_voltage", device_id=None): SensorDescription(
device_key=DeviceKey(key="battery_voltage", device_id=None),
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=Units.ELECTRIC_POTENTIAL_VOLT,
),
DeviceKey(key="tank_level", device_id=None): SensorDescription(
device_key=DeviceKey(key="tank_level", device_id=None),
device_class=SensorDeviceClass.DISTANCE,
native_unit_of_measurement=Units.LENGTH_MILLIMETERS,
),
DeviceKey(key="temperature", device_id=None): SensorDescription(
device_key=DeviceKey(key="temperature", device_id=None),
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=Units.TEMP_CELSIUS,
),
DeviceKey(key="battery", device_id=None): SensorDescription(
device_key=DeviceKey(key="battery", device_id=None),
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=Units.PERCENTAGE,
),
DeviceKey(key="signal_strength", device_id=None): SensorDescription(
device_key=DeviceKey(key="signal_strength", device_id=None),
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
),
DeviceKey(key="reading_quality", device_id=None): SensorDescription(
device_key=DeviceKey(key="reading_quality", device_id=None),
device_class=None,
native_unit_of_measurement=Units.PERCENTAGE,
),
DeviceKey(key="reading_quality_raw", device_id=None): SensorDescription(
device_key=DeviceKey(key="reading_quality_raw", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
DeviceKey(key="accelerometer_y", device_id=None): SensorDescription(
device_key=DeviceKey(key="accelerometer_y", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
DeviceKey(key="accelerometer_x", device_id=None): SensorDescription(
device_key=DeviceKey(key="accelerometer_x", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
},
entity_values={
DeviceKey(key="battery_voltage", device_id=None): SensorValue(
device_key=DeviceKey(key="battery_voltage", device_id=None),
name="Battery Voltage",
native_value=3.5,
),
DeviceKey(key="tank_level", device_id=None): SensorValue(
device_key=DeviceKey(key="tank_level", device_id=None),
name="Tank Level",
native_value=341,
),
DeviceKey(key="temperature", device_id=None): SensorValue(
device_key=DeviceKey(key="temperature", device_id=None),
name="Temperature",
native_value=27,
),
DeviceKey(key="battery", device_id=None): SensorValue(
device_key=DeviceKey(key="battery", device_id=None),
name="Battery",
native_value=100,
),
DeviceKey(key="signal_strength", device_id=None): SensorValue(
device_key=DeviceKey(key="signal_strength", device_id=None),
name="Signal Strength",
native_value=-63,
),
DeviceKey(key="reading_quality", device_id=None): SensorValue(
device_key=DeviceKey(key="reading_quality", device_id=None),
name="Reading quality",
native_value=100,
),
DeviceKey(key="reading_quality_raw", device_id=None): SensorValue(
device_key=DeviceKey(key="reading_quality_raw", device_id=None),
name="Reading quality raw",
native_value=3,
),
DeviceKey(key="accelerometer_y", device_id=None): SensorValue(
device_key=DeviceKey(key="accelerometer_y", device_id=None),
name="Position Y",
native_value=227,
),
DeviceKey(key="accelerometer_x", device_id=None): SensorValue(
device_key=DeviceKey(key="accelerometer_x", device_id=None),
name="Position X",
native_value=250,
),
},
binary_entity_descriptions={
DeviceKey(key="button_pressed", device_id=None): BinarySensorDescription(
device_key=DeviceKey(key="button_pressed", device_id=None),
device_class=BinarySensorDeviceClass.OCCUPANCY,
)
},
binary_entity_values={
DeviceKey(key="button_pressed", device_id=None): BinarySensorValue(
device_key=DeviceKey(key="button_pressed", device_id=None),
name="Button pressed",
native_value=False,
)
},
events={},
)
def test_pro_200b():
parser = MopekaIOTBluetoothDeviceData()
service_info = PRO_200B_SERVICE_INFO
result = parser.update(service_info)
assert result == SensorUpdate(
title=None,
devices={
None: SensorDeviceInfo(
name="Pro-200B E52B",
model="Pro-200",
manufacturer="Mopeka IOT",
sw_version=None,
hw_version=None,
)
},
entity_descriptions={
DeviceKey(key="battery_voltage", device_id=None): SensorDescription(
device_key=DeviceKey(key="battery_voltage", device_id=None),
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=Units.ELECTRIC_POTENTIAL_VOLT,
),
DeviceKey(key="tank_level", device_id=None): SensorDescription(
device_key=DeviceKey(key="tank_level", device_id=None),
device_class=SensorDeviceClass.DISTANCE,
native_unit_of_measurement=Units.LENGTH_MILLIMETERS,
),
DeviceKey(key="temperature", device_id=None): SensorDescription(
device_key=DeviceKey(key="temperature", device_id=None),
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=Units.TEMP_CELSIUS,
),
DeviceKey(key="battery", device_id=None): SensorDescription(
device_key=DeviceKey(key="battery", device_id=None),
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=Units.PERCENTAGE,
),
DeviceKey(key="signal_strength", device_id=None): SensorDescription(
device_key=DeviceKey(key="signal_strength", device_id=None),
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
),
DeviceKey(key="reading_quality", device_id=None): SensorDescription(
device_key=DeviceKey(key="reading_quality", device_id=None),
device_class=None,
native_unit_of_measurement=Units.PERCENTAGE,
),
DeviceKey(key="reading_quality_raw", device_id=None): SensorDescription(
device_key=DeviceKey(key="reading_quality_raw", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
DeviceKey(key="accelerometer_y", device_id=None): SensorDescription(
device_key=DeviceKey(key="accelerometer_y", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
DeviceKey(key="accelerometer_x", device_id=None): SensorDescription(
device_key=DeviceKey(key="accelerometer_x", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
},
entity_values={
DeviceKey(key="battery_voltage", device_id=None): SensorValue(
device_key=DeviceKey(key="battery_voltage", device_id=None),
name="Battery Voltage",
native_value=2.96875,
),
DeviceKey(key="tank_level", device_id=None): SensorValue(
device_key=DeviceKey(key="tank_level", device_id=None),
name="Tank Level",
native_value=806,
),
DeviceKey(key="temperature", device_id=None): SensorValue(
device_key=DeviceKey(key="temperature", device_id=None),
name="Temperature",
native_value=9,
),
DeviceKey(key="battery", device_id=None): SensorValue(
device_key=DeviceKey(key="battery", device_id=None),
name="Battery",
native_value=100,
),
DeviceKey(key="signal_strength", device_id=None): SensorValue(
device_key=DeviceKey(key="signal_strength", device_id=None),
name="Signal Strength",
native_value=-63,
),
DeviceKey(key="reading_quality", device_id=None): SensorValue(
device_key=DeviceKey(key="reading_quality", device_id=None),
name="Reading quality",
native_value=100,
),
DeviceKey(key="reading_quality_raw", device_id=None): SensorValue(
device_key=DeviceKey(key="reading_quality_raw", device_id=None),
name="Reading quality raw",
native_value=3,
),
DeviceKey(key="accelerometer_y", device_id=None): SensorValue(
device_key=DeviceKey(key="accelerometer_y", device_id=None),
name="Position Y",
native_value=63,
),
DeviceKey(key="accelerometer_x", device_id=None): SensorValue(
device_key=DeviceKey(key="accelerometer_x", device_id=None),
name="Position X",
native_value=168,
),
},
binary_entity_descriptions={
DeviceKey(key="button_pressed", device_id=None): BinarySensorDescription(
device_key=DeviceKey(key="button_pressed", device_id=None),
device_class=BinarySensorDeviceClass.OCCUPANCY,
)
},
binary_entity_values={
DeviceKey(key="button_pressed", device_id=None): BinarySensorValue(
device_key=DeviceKey(key="button_pressed", device_id=None),
name="Button pressed",
native_value=False,
)
},
events={},
)
@pytest.mark.parametrize(
"battery_raw, expected_voltage",
[
(89, 2.78125),
(90, 2.8125),
(91, 2.84375),
(92, 2.875),
(93, 2.90625),
(94, 2.9375),
(95, 2.96875),
(96, 3.0),
(97, 3.03125),
(98, 3.0625),
(99, 3.09375),
(100, 3.125),
],
)
def test_battery_to_voltage(battery_raw: int, expected_voltage: float) -> None:
voltage = battery_to_voltage(battery_raw)
assert voltage == expected_voltage
def test_battery_to_percentage():
battery_raw = 89 # example battery raw value
percentage = battery_to_percentage(battery_raw)
assert percentage == 89.4
def test_temp_to_celsius():
temperature_raw = 77 # example temperature raw value
celsius = temp_to_celsius(temperature_raw)
assert celsius == 37
def test_tank_level_to_mm():
tank_level_raw = 3145 # example tank level raw value
mm = tank_level_to_mm(tank_level_raw)
assert mm == 31450 # tank_level_raw * 10
def test_tank_level_and_temp_to_mm():
temperature_raw = 77 # example temperature raw value
tank_level_raw = 3145 # example tank level raw value
tank_level_mm = tank_level_and_temp_to_mm(
tank_level_raw, temperature_raw, MediumType.AIR
)
expected_mm = int(
tank_level_raw
* (
0.153096 # coefs[0] for MediumType.AIR
+ (0.000327 * temperature_raw) # coefs[1] * temp
+ (-0.000000294 * (temperature_raw**2)) # coefs[2] * (temp ** 2)
)
)
assert tank_level_mm == expected_mm
def test_parser_with_sample_data():
medium_type = MediumType.AIR
battery_raw = 89 # example battery raw value
tank_level_raw = 3145 # example tank level raw value
temperature_raw = 77 # example temperature raw value
assert MopekaIOTBluetoothDeviceData(medium_type)
assert battery_to_voltage(battery_raw) == 2.78125
assert battery_to_percentage(battery_raw) == 89.4
assert temp_to_celsius(temperature_raw) == 37
assert tank_level_and_temp_to_mm(
tank_level_raw, temperature_raw, medium_type
) == int(
tank_level_raw
* (
0.153096 # coefs[0] for MediumType.AIR
+ (0.000327 * temperature_raw) # coefs[1] * temp
+ (-0.000000294 * (temperature_raw**2)) # coefs[2] * (temp ** 2)
)
)
# Test entire parser chain
def test_tdr40_air_bad_quality():
parser = MopekaIOTBluetoothDeviceData(MediumType.AIR)
service_info = TDR40_AIR_BAD_QUALITY_INFO
result = parser.update(service_info)
assert result == SensorUpdate(
title=None,
devices={
None: SensorDeviceInfo(
name="TD40/TD200 7510", # Corrected name
model="TD40/TD200", # Corrected model
manufacturer="Mopeka IOT",
sw_version=None,
hw_version=None,
)
},
entity_descriptions={
DeviceKey(key="battery_voltage", device_id=None): SensorDescription(
device_key=DeviceKey(key="battery_voltage", device_id=None),
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=Units.ELECTRIC_POTENTIAL_VOLT,
),
DeviceKey(key="tank_level", device_id=None): SensorDescription(
device_key=DeviceKey(key="tank_level", device_id=None),
device_class=SensorDeviceClass.DISTANCE,
native_unit_of_measurement=Units.LENGTH_MILLIMETERS,
),
DeviceKey(key="temperature", device_id=None): SensorDescription(
device_key=DeviceKey(key="temperature", device_id=None),
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=Units.TEMP_CELSIUS,
),
DeviceKey(key="battery", device_id=None): SensorDescription(
device_key=DeviceKey(key="battery", device_id=None),
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=Units.PERCENTAGE,
),
DeviceKey(key="signal_strength", device_id=None): SensorDescription(
device_key=DeviceKey(key="signal_strength", device_id=None),
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
),
DeviceKey(key="reading_quality", device_id=None): SensorDescription(
device_key=DeviceKey(key="reading_quality", device_id=None),
device_class=None,
native_unit_of_measurement=Units.PERCENTAGE,
),
DeviceKey(key="reading_quality_raw", device_id=None): SensorDescription(
device_key=DeviceKey(key="reading_quality_raw", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
DeviceKey(key="accelerometer_y", device_id=None): SensorDescription(
device_key=DeviceKey(key="accelerometer_y", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
DeviceKey(key="accelerometer_x", device_id=None): SensorDescription(
device_key=DeviceKey(key="accelerometer_x", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
},
entity_values={
DeviceKey(key="battery_voltage", device_id=None): SensorValue(
device_key=DeviceKey(key="battery_voltage", device_id=None),
name="Battery Voltage",
native_value=3.59375,
),
DeviceKey(key="tank_level", device_id=None): SensorValue(
device_key=DeviceKey(key="tank_level", device_id=None),
name="Tank Level",
native_value=588,
),
DeviceKey(key="temperature", device_id=None): SensorValue(
device_key=DeviceKey(key="temperature", device_id=None),
name="Temperature",
native_value=24,
),
DeviceKey(key="battery", device_id=None): SensorValue(
device_key=DeviceKey(key="battery", device_id=None),
name="Battery",
native_value=100,
),
DeviceKey(key="signal_strength", device_id=None): SensorValue(
device_key=DeviceKey(key="signal_strength", device_id=None),
name="Signal Strength",
native_value=-60,
),
DeviceKey(key="reading_quality", device_id=None): SensorValue(
device_key=DeviceKey(key="reading_quality", device_id=None),
name="Reading quality",
native_value=33,
),
DeviceKey(key="reading_quality_raw", device_id=None): SensorValue(
device_key=DeviceKey(key="reading_quality_raw", device_id=None),
name="Reading quality raw",
native_value=1,
),
DeviceKey(key="accelerometer_y", device_id=None): SensorValue(
device_key=DeviceKey(key="accelerometer_y", device_id=None),
name="Position Y",
native_value=128,
),
DeviceKey(key="accelerometer_x", device_id=None): SensorValue(
device_key=DeviceKey(key="accelerometer_x", device_id=None),
name="Position X",
native_value=127,
),
},
binary_entity_descriptions={
DeviceKey(key="button_pressed", device_id=None): BinarySensorDescription(
device_key=DeviceKey(key="button_pressed", device_id=None),
device_class=BinarySensorDeviceClass.OCCUPANCY,
)
},
binary_entity_values={
DeviceKey(key="button_pressed", device_id=None): BinarySensorValue(
device_key=DeviceKey(key="button_pressed", device_id=None),
name="Button pressed",
native_value=False,
)
},
events={},
)
def test_tdr40_air_low_quality():
parser = MopekaIOTBluetoothDeviceData(MediumType.AIR)
result = parser.update(TDR40_AIR_LOW_QUALITY_INFO_2)
assert result == SensorUpdate(
title=None,
devices={
None: SensorDeviceInfo(
name="Pro Check Universal 7510", # Updated name
model="M1017", # Updated model
manufacturer="Mopeka IOT",
sw_version=None,
hw_version=None,
)
},
entity_descriptions={
DeviceKey(key="battery_voltage", device_id=None): SensorDescription(
device_key=DeviceKey(key="battery_voltage", device_id=None),
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=Units.ELECTRIC_POTENTIAL_VOLT,
),
DeviceKey(key="tank_level", device_id=None): SensorDescription(
device_key=DeviceKey(key="tank_level", device_id=None),
device_class=SensorDeviceClass.DISTANCE,
native_unit_of_measurement=Units.LENGTH_MILLIMETERS,
),
DeviceKey(key="temperature", device_id=None): SensorDescription(
device_key=DeviceKey(key="temperature", device_id=None),
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=Units.TEMP_CELSIUS,
),
DeviceKey(key="battery", device_id=None): SensorDescription(
device_key=DeviceKey(key="battery", device_id=None),
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=Units.PERCENTAGE,
),
DeviceKey(key="signal_strength", device_id=None): SensorDescription(
device_key=DeviceKey(key="signal_strength", device_id=None),
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
),
DeviceKey(key="reading_quality", device_id=None): SensorDescription(
device_key=DeviceKey(key="reading_quality", device_id=None),
device_class=None,
native_unit_of_measurement=Units.PERCENTAGE,
),
DeviceKey(key="reading_quality_raw", device_id=None): SensorDescription(
device_key=DeviceKey(key="reading_quality_raw", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
DeviceKey(key="accelerometer_y", device_id=None): SensorDescription(
device_key=DeviceKey(key="accelerometer_y", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
DeviceKey(key="accelerometer_x", device_id=None): SensorDescription(
device_key=DeviceKey(key="accelerometer_x", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
},
entity_values={
DeviceKey(key="battery_voltage", device_id=None): SensorValue(
device_key=DeviceKey(key="battery_voltage", device_id=None),
name="Battery Voltage",
native_value=3.0,
),
DeviceKey(key="tank_level", device_id=None): SensorValue(
device_key=DeviceKey(key="tank_level", device_id=None),
name="Tank Level",
native_value=141,
),
DeviceKey(key="temperature", device_id=None): SensorValue(
device_key=DeviceKey(key="temperature", device_id=None),
name="Temperature",
native_value=16,
),
DeviceKey(key="battery", device_id=None): SensorValue(
device_key=DeviceKey(key="battery", device_id=None),
name="Battery",
native_value=100,
),
DeviceKey(key="signal_strength", device_id=None): SensorValue(
device_key=DeviceKey(key="signal_strength", device_id=None),
name="Signal Strength",
native_value=-49,
),
DeviceKey(key="reading_quality", device_id=None): SensorValue(
device_key=DeviceKey(key="reading_quality", device_id=None),
name="Reading quality",
native_value=67,
),
DeviceKey(key="reading_quality_raw", device_id=None): SensorValue(
device_key=DeviceKey(key="reading_quality_raw", device_id=None),
name="Reading quality raw",
native_value=2,
),
DeviceKey(key="accelerometer_y", device_id=None): SensorValue(
device_key=DeviceKey(key="accelerometer_y", device_id=None),
name="Position Y",
native_value=248,
),
DeviceKey(key="accelerometer_x", device_id=None): SensorValue(
device_key=DeviceKey(key="accelerometer_x", device_id=None),
name="Position X",
native_value=49,
),
},
binary_entity_descriptions={
DeviceKey(key="button_pressed", device_id=None): BinarySensorDescription(
device_key=DeviceKey(key="button_pressed", device_id=None),
device_class=BinarySensorDeviceClass.OCCUPANCY,
)
},
binary_entity_values={
DeviceKey(key="button_pressed", device_id=None): BinarySensorValue(
device_key=DeviceKey(key="button_pressed", device_id=None),
name="Button pressed",
native_value=False,
)
},
events={},
)
def test_tdr40_air_good_quality():
parser = MopekaIOTBluetoothDeviceData(MediumType.AIR)
service_info = TDR40_AIR_GOOD_QUALITY_INFO
result = parser.update(service_info)
assert result == SensorUpdate(
title=None,
devices={
None: SensorDeviceInfo(
name="TD40/TD200 7510",
model="TD40/TD200",
manufacturer="Mopeka IOT",
sw_version=None,
hw_version=None,
)
},
entity_descriptions={
DeviceKey(key="battery_voltage", device_id=None): SensorDescription(
device_key=DeviceKey(key="battery_voltage", device_id=None),
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=Units.ELECTRIC_POTENTIAL_VOLT,
),
DeviceKey(key="tank_level", device_id=None): SensorDescription(
device_key=DeviceKey(key="tank_level", device_id=None),
device_class=SensorDeviceClass.DISTANCE,
native_unit_of_measurement=Units.LENGTH_MILLIMETERS,
),
DeviceKey(key="temperature", device_id=None): SensorDescription(
device_key=DeviceKey(key="temperature", device_id=None),
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=Units.TEMP_CELSIUS,
),
DeviceKey(key="battery", device_id=None): SensorDescription(
device_key=DeviceKey(key="battery", device_id=None),
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=Units.PERCENTAGE,
),
DeviceKey(key="signal_strength", device_id=None): SensorDescription(
device_key=DeviceKey(key="signal_strength", device_id=None),
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
),
DeviceKey(key="reading_quality", device_id=None): SensorDescription(
device_key=DeviceKey(key="reading_quality", device_id=None),
device_class=None,
native_unit_of_measurement=Units.PERCENTAGE,
),
DeviceKey(key="reading_quality_raw", device_id=None): SensorDescription(
device_key=DeviceKey(key="reading_quality_raw", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
DeviceKey(key="accelerometer_y", device_id=None): SensorDescription(
device_key=DeviceKey(key="accelerometer_y", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
DeviceKey(key="accelerometer_x", device_id=None): SensorDescription(
device_key=DeviceKey(key="accelerometer_x", device_id=None),
device_class=None,
native_unit_of_measurement=None,
),
},
entity_values={
DeviceKey(key="battery_voltage", device_id=None): SensorValue(
device_key=DeviceKey(key="battery_voltage", device_id=None),
name="Battery Voltage",
native_value=3.53125,
),
DeviceKey(key="tank_level", device_id=None): SensorValue(
device_key=DeviceKey(key="tank_level", device_id=None),
name="Tank Level",
native_value=729,
),
DeviceKey(key="temperature", device_id=None): SensorValue(
device_key=DeviceKey(key="temperature", device_id=None),
name="Temperature",
native_value=24,
),
DeviceKey(key="battery", device_id=None): SensorValue(
device_key=DeviceKey(key="battery", device_id=None),
name="Battery",
native_value=100,
),
DeviceKey(key="signal_strength", device_id=None): SensorValue(
device_key=DeviceKey(key="signal_strength", device_id=None),
name="Signal Strength",
native_value=-50,
),
DeviceKey(key="reading_quality", device_id=None): SensorValue(
device_key=DeviceKey(key="reading_quality", device_id=None),
name="Reading quality",
native_value=100,
),
DeviceKey(key="reading_quality_raw", device_id=None): SensorValue(
device_key=DeviceKey(key="reading_quality_raw", device_id=None),
name="Reading quality raw",
native_value=3,
),
DeviceKey(key="accelerometer_y", device_id=None): SensorValue(
device_key=DeviceKey(key="accelerometer_y", device_id=None),
name="Position Y",
native_value=32,
),
DeviceKey(key="accelerometer_x", device_id=None): SensorValue(
device_key=DeviceKey(key="accelerometer_x", device_id=None),
name="Position X",
native_value=128,
),
},
binary_entity_descriptions={
DeviceKey(key="button_pressed", device_id=None): BinarySensorDescription(
device_key=DeviceKey(key="button_pressed", device_id=None),
device_class=BinarySensorDeviceClass.OCCUPANCY,
)
},
binary_entity_values={
DeviceKey(key="button_pressed", device_id=None): BinarySensorValue(
device_key=DeviceKey(key="button_pressed", device_id=None),
name="Button pressed",
native_value=False,
)
},
events={},
)