diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1780fce..0a47942 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,15 +36,10 @@ jobs: fail-fast: false matrix: python-version: - - "3.8" - - "3.9" - - "3.10" - "3.11" - "3.12" os: - ubuntu-latest - - windows-latest - - macOS-latest runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 @@ -85,14 +80,14 @@ jobs: # Do a dry run of PSR - name: Test release - uses: python-semantic-release/python-semantic-release@v8.7.2 + uses: bluetooth-devices/python-semantic-release@311 if: github.ref_name != 'main' with: root_options: --noop # On main branch: actual PSR + upload to PyPI & GitHub - name: Release - uses: python-semantic-release/python-semantic-release@v8.7.2 + uses: bluetooth-devices/python-semantic-release@311 id: release if: github.ref_name == 'main' with: diff --git a/.github/workflows/poetry-upgrade.yml b/.github/workflows/poetry-upgrade.yml index 9ba349c..1de317c 100644 --- a/.github/workflows/poetry-upgrade.yml +++ b/.github/workflows/poetry-upgrade.yml @@ -3,7 +3,7 @@ name: Upgrader on: workflow_dispatch: schedule: - - cron: "51 21 26 * *" + - cron: "19 3 1 * *" jobs: upgrade: diff --git a/poetry.lock b/poetry.lock index b80603f..b8424cb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -22,9 +22,6 @@ files = [ {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, ] -[package.dependencies] -pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} - [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] @@ -49,6 +46,66 @@ charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] +[[package]] +name = "bluetooth-data-tools" +version = "1.19.0" +description = "Tools for converting bluetooth data and packets" +optional = false +python-versions = ">=3.10,<4.0" +files = [ + {file = "bluetooth_data_tools-1.19.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:cc9130b92fe5a11ba91b4632cb5b5c5bdf9dfe8c4741f610ae277c971c226a11"}, + {file = "bluetooth_data_tools-1.19.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c0137966b6a1e3b95b50fa81b64c53d87ab41ab53d9aab69bad4113c7311afc"}, + {file = "bluetooth_data_tools-1.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c98115130a41122360b799aa7b3492ccea45bb5095ac16bb7b727111737ea5ce"}, + {file = "bluetooth_data_tools-1.19.0-cp310-cp310-manylinux_2_31_x86_64.whl", hash = "sha256:a3de1c93f210bcae0048fa0ebc96b097613eddcbea5b05824df8b8fc90d343f8"}, + {file = "bluetooth_data_tools-1.19.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1b57bfbb878decfda85816b0744d6a18c82a353f6531ef5b5037a4088865c7de"}, + {file = "bluetooth_data_tools-1.19.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:252fa800bfc22de0cc19e491e99e155305a47788cff6c31d56450f6137c74f6b"}, + {file = "bluetooth_data_tools-1.19.0-cp310-cp310-win32.whl", hash = "sha256:541adf9b8a093c4333f7c4b99e2662ec7c38a9dde8b2561d5e280da06573707a"}, + {file = "bluetooth_data_tools-1.19.0-cp310-cp310-win_amd64.whl", hash = "sha256:74843706ad65dbc0c32cf4ce073e266353f48970ab0a6ae2edda1969fbb318d1"}, + {file = "bluetooth_data_tools-1.19.0-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:2e36ec53e6264faa9a75e8b704ce8f274ab0ba336ce3e5b7a279c254b2231bc2"}, + {file = "bluetooth_data_tools-1.19.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ebd2783c1f487d2c991f4f2ed399ea452fdee605e4de4417f353a5bee494b82"}, + {file = "bluetooth_data_tools-1.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:238db5bf2f19872c1318fcc35a4efc48961065ff7c227a0d239b220de1576d80"}, + {file = "bluetooth_data_tools-1.19.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bae1f9a2a1a89c5268c796d32c6a000d783ad9fc73fa9a271a287c6eaa1ecd97"}, + {file = "bluetooth_data_tools-1.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9763c3d3df14d16884775d61f034499afa4a32b8538cb6f2c71630ef6967295b"}, + {file = "bluetooth_data_tools-1.19.0-cp311-cp311-win32.whl", hash = "sha256:d23997cc3d849bfdf85ef42645b999054e4913a320ad10e77ca7557806008768"}, + {file = "bluetooth_data_tools-1.19.0-cp311-cp311-win_amd64.whl", hash = "sha256:4b73b3509497d642404c8efc2b42de3b34d06172a88dbeacc6f8ebc1de1e550f"}, + {file = "bluetooth_data_tools-1.19.0-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:8371aeb50cef225a17df9a013277b3363ca59a53097cec5cb1ec89dea8323c3c"}, + {file = "bluetooth_data_tools-1.19.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:586bdeca2e24a41ceddad7fa02a3ed7a9c865f13ee95e8231b6a56c0a07e3e87"}, + {file = "bluetooth_data_tools-1.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56f6f822f2d69a620151f2a0bb9ad8502e5c59f84cff473befe545709bd952b5"}, + {file = "bluetooth_data_tools-1.19.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8c6f4c95590c1524bf1cb3e543ee48760965b9eb5a2944b90e82f42200fce759"}, + {file = "bluetooth_data_tools-1.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:72188dd239728d6845d759671733d6b4cbf37face2adfd844fdcf431fce4103f"}, + {file = "bluetooth_data_tools-1.19.0-cp312-cp312-win32.whl", hash = "sha256:30acd97edc15d97eacae205b2c495371d7ae8cd878b3d72424286935ebd7d857"}, + {file = "bluetooth_data_tools-1.19.0-cp312-cp312-win_amd64.whl", hash = "sha256:ce08ad5d2ce7ea624388bf5263c2688c356fae45c583d7331439b489f4191f01"}, + {file = "bluetooth_data_tools-1.19.0-pp310-pypy310_pp73-macosx_11_0_x86_64.whl", hash = "sha256:8e81410105a4b753fb4afc6a881fa666c0dcba70fb38fe26835f4bf8baa71cd4"}, + {file = "bluetooth_data_tools-1.19.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c183ed080682cd4ab9f84205d648c46b9d65d41f4d9bbd65dc3a4787c07e633c"}, + {file = "bluetooth_data_tools-1.19.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e6dde7df8f61f13bb2e6bff29a93a190f31a858d753c48986c66c9aa3bdd3c2"}, + {file = "bluetooth_data_tools-1.19.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:6e5d1546c3a2d400d78c70ededc49c4f7b418386b4b1f68611871ec3a39a8c85"}, + {file = "bluetooth_data_tools-1.19.0.tar.gz", hash = "sha256:5b58349126efbb38d61222267cf85bf1766f832cc5744a59e70a1a2653e477d9"}, +] + +[package.dependencies] +cryptography = ">=41.0.3" + +[package.extras] +docs = ["Sphinx (>=5.0,<6.0)", "myst-parser (>=0.18,<0.19)", "sphinx-rtd-theme (>=1.0,<2.0)"] + +[[package]] +name = "bluetooth-sensor-state-data" +version = "1.6.2" +description = "Models for storing and converting Bluetooth Sensor State Data" +optional = false +python-versions = ">=3.9,<4.0" +files = [ + {file = "bluetooth_sensor_state_data-1.6.2-py3-none-any.whl", hash = "sha256:27104d7888ef571402e06d9a3886ad4bbbe5131e977abd4dd3089052751d463e"}, + {file = "bluetooth_sensor_state_data-1.6.2.tar.gz", hash = "sha256:08de88540658dbc13184705a1be85d1904efbb17d9f74db94538b26dcb580a1c"}, +] + +[package.dependencies] +home-assistant-bluetooth = ">=1.3.0" +sensor-state-data = ">=2.0" + +[package.extras] +docs = ["Sphinx (>=5.0,<6.0)", "myst-parser (>=0.18,<0.19)", "sphinx-rtd-theme (>=1.0,<2.0)"] + [[package]] name = "certifi" version = "2023.11.17" @@ -60,6 +117,70 @@ files = [ {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, ] +[[package]] +name = "cffi" +version = "1.16.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, +] + +[package.dependencies] +pycparser = "*" + [[package]] name = "charset-normalizer" version = "3.3.2" @@ -231,37 +352,65 @@ files = [ {file = "coverage-7.4.0.tar.gz", hash = "sha256:707c0f58cb1712b8809ece32b68996ee1e609f71bd14615bd8f87a1293cb610e"}, ] -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - [package.extras] toml = ["tomli"] [[package]] -name = "docutils" -version = "0.20.1" -description = "Docutils -- Python Documentation Utilities" +name = "cryptography" +version = "41.0.7" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, - {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, + {file = "cryptography-41.0.7-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:3c78451b78313fa81607fa1b3f1ae0a5ddd8014c38a02d9db0616133987b9cdf"}, + {file = "cryptography-41.0.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:928258ba5d6f8ae644e764d0f996d61a8777559f72dfeb2eea7e2fe0ad6e782d"}, + {file = "cryptography-41.0.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a1b41bc97f1ad230a41657d9155113c7521953869ae57ac39ac7f1bb471469a"}, + {file = "cryptography-41.0.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:841df4caa01008bad253bce2a6f7b47f86dc9f08df4b433c404def869f590a15"}, + {file = "cryptography-41.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5429ec739a29df2e29e15d082f1d9ad683701f0ec7709ca479b3ff2708dae65a"}, + {file = "cryptography-41.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:43f2552a2378b44869fe8827aa19e69512e3245a219104438692385b0ee119d1"}, + {file = "cryptography-41.0.7-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:af03b32695b24d85a75d40e1ba39ffe7db7ffcb099fe507b39fd41a565f1b157"}, + {file = "cryptography-41.0.7-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:49f0805fc0b2ac8d4882dd52f4a3b935b210935d500b6b805f321addc8177406"}, + {file = "cryptography-41.0.7-cp37-abi3-win32.whl", hash = "sha256:f983596065a18a2183e7f79ab3fd4c475205b839e02cbc0efbbf9666c4b3083d"}, + {file = "cryptography-41.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:90452ba79b8788fa380dfb587cca692976ef4e757b194b093d845e8d99f612f2"}, + {file = "cryptography-41.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:079b85658ea2f59c4f43b70f8119a52414cdb7be34da5d019a77bf96d473b960"}, + {file = "cryptography-41.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:b640981bf64a3e978a56167594a0e97db71c89a479da8e175d8bb5be5178c003"}, + {file = "cryptography-41.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e3114da6d7f95d2dee7d3f4eec16dacff819740bbab931aff8648cb13c5ff5e7"}, + {file = "cryptography-41.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d5ec85080cce7b0513cfd233914eb8b7bbd0633f1d1703aa28d1dd5a72f678ec"}, + {file = "cryptography-41.0.7-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7a698cb1dac82c35fcf8fe3417a3aaba97de16a01ac914b89a0889d364d2f6be"}, + {file = "cryptography-41.0.7-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:37a138589b12069efb424220bf78eac59ca68b95696fc622b6ccc1c0a197204a"}, + {file = "cryptography-41.0.7-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:68a2dec79deebc5d26d617bfdf6e8aab065a4f34934b22d3b5010df3ba36612c"}, + {file = "cryptography-41.0.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:09616eeaef406f99046553b8a40fbf8b1e70795a91885ba4c96a70793de5504a"}, + {file = "cryptography-41.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48a0476626da912a44cc078f9893f292f0b3e4c739caf289268168d8f4702a39"}, + {file = "cryptography-41.0.7-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c7f3201ec47d5207841402594f1d7950879ef890c0c495052fa62f58283fde1a"}, + {file = "cryptography-41.0.7-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c5ca78485a255e03c32b513f8c2bc39fedb7f5c5f8535545bdc223a03b24f248"}, + {file = "cryptography-41.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d6c391c021ab1f7a82da5d8d0b3cee2f4b2c455ec86c8aebbc84837a631ff309"}, + {file = "cryptography-41.0.7.tar.gz", hash = "sha256:13f93ce9bea8016c253b34afc6bd6a75993e5c40672ed5405a9c832f0d4a00bc"}, ] +[package.dependencies] +cffi = ">=1.12" + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] +nox = ["nox"] +pep8test = ["black", "check-sdist", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + [[package]] -name = "exceptiongroup" -version = "1.2.0" -description = "Backport of PEP 654 (exception groups)" +name = "docutils" +version = "0.20.1" +description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, - {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, + {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, + {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, ] -[package.extras] -test = ["pytest (>=6)"] - [[package]] name = "furo" version = "2023.9.10" @@ -279,6 +428,17 @@ pygments = ">=2.7" sphinx = ">=6.0,<8.0" sphinx-basic-ng = "*" +[[package]] +name = "home-assistant-bluetooth" +version = "1.10.4" +description = "Home Assistant Bluetooth Models and Helpers" +optional = false +python-versions = ">=3.9,<4.0" +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 = "idna" version = "3.6" @@ -301,25 +461,6 @@ files = [ {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] -[[package]] -name = "importlib-metadata" -version = "7.0.1" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"}, - {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"}, -] - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] - [[package]] name = "iniconfig" version = "2.0.0" @@ -538,6 +679,17 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + [[package]] name = "pygments" version = "2.17.2" @@ -566,11 +718,9 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] @@ -593,17 +743,6 @@ pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] -[[package]] -name = "pytz" -version = "2023.3.post1" -description = "World timezone definitions, modern and historical" -optional = false -python-versions = "*" -files = [ - {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, - {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, -] - [[package]] name = "pyyaml" version = "6.0.1" @@ -684,6 +823,20 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "sensor-state-data" +version = "2.18.0" +description = "Models for storing and converting Sensor Data state" +optional = false +python-versions = ">=3.9,<4.0" +files = [ + {file = "sensor_state_data-2.18.0-py3-none-any.whl", hash = "sha256:a079ad856fb28ba4d49f967eff6448252e21c7088d7ae53762c9f11728d15777"}, + {file = "sensor_state_data-2.18.0.tar.gz", hash = "sha256:4876e8e3400523a8ec8d1cbf087d7f55246817f53d11bc45a9e3f4d56a3d7d34"}, +] + +[package.extras] +docs = ["Sphinx (>=5.0,<6.0)", "myst-parser (>=0.18,<0.19)", "sphinx-rtd-theme (>=1.0,<2.0)"] + [[package]] name = "six" version = "1.16.0" @@ -734,7 +887,6 @@ babel = ">=2.9" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} docutils = ">=0.18.1,<0.21" imagesize = ">=1.3" -importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} Jinja2 = ">=3.0" packaging = ">=21.0" Pygments = ">=2.13" @@ -877,17 +1029,6 @@ files = [ lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - [[package]] name = "tornado" version = "6.4" @@ -924,22 +1065,7 @@ brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] -[[package]] -name = "zipp" -version = "3.17.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.8" -files = [ - {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, - {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] - [metadata] lock-version = "2.0" -python-versions = "^3.8" -content-hash = "bed14133e1b6bab9dbd04fbac961912c2d67b60da6884b0694bce0c3d0fa7dc2" +python-versions = "^3.11" +content-hash = "9c1857e873a2fd87928ecb52c815398c6e7d6fa3a941ca8101f3245c7af6627e" diff --git a/pyproject.toml b/pyproject.toml index b4c45ab..98ed61b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,11 @@ packages = [ "Changelog" = "https://github.com/bluetooth-devices/leaone-ble/blob/main/CHANGELOG.md" [tool.poetry.dependencies] -python = "^3.8" +python = "^3.11" +bluetooth-sensor-state-data = ">=1.6.2" +home-assistant-bluetooth = ">=1.10.4" +sensor-state-data = ">=2.18.0" +bluetooth-data-tools = ">=1.19.0" [tool.poetry.group.dev.dependencies] pytest = "^7.0" diff --git a/src/leaone_ble/__init__.py b/src/leaone_ble/__init__.py index 6c8e6b9..a3aaa24 100644 --- a/src/leaone_ble/__init__.py +++ b/src/leaone_ble/__init__.py @@ -1 +1,35 @@ -__version__ = "0.0.0" +""" +Parser for Leaone BLE advertisements. + +This file is shamelessly copied from the following repository: +https://github.com/Ernst79/bleparser/blob/c42ae922e1abed2720c7fac993777e1bd59c0c93/package/bleparser/leaone.py + +MIT License applies. +""" +from __future__ import annotations + +from sensor_state_data import ( + DeviceClass, + DeviceKey, + SensorDescription, + SensorDeviceInfo, + SensorUpdate, + SensorValue, + Units, +) + +from .parser import LeaoneBluetoothDeviceData + +__version__ = "0.1.1" + +__all__ = [ + "LeaoneBluetoothDeviceData", + "SensorDescription", + "SensorDeviceInfo", + "DeviceClass", + "DeviceKey", + "SensorUpdate", + "SensorDeviceInfo", + "SensorValue", + "Units", +] diff --git a/src/leaone_ble/main.py b/src/leaone_ble/main.py deleted file mode 100644 index 8400ef8..0000000 --- a/src/leaone_ble/main.py +++ /dev/null @@ -1,3 +0,0 @@ -def add(n1: int, n2: int) -> int: - """Add the arguments.""" - return n1 + n2 diff --git a/src/leaone_ble/parser.py b/src/leaone_ble/parser.py new file mode 100644 index 0000000..1d11f5e --- /dev/null +++ b/src/leaone_ble/parser.py @@ -0,0 +1,124 @@ +""" +Parser for Leaone BLE advertisements. + +This file is shamelessly copied from the following repository: +https://github.com/Ernst79/bleparser/blob/c42ae922e1abed2720c7fac993777e1bd59c0c93/package/bleparser/xiaogui.py + +MIT License applies. +""" +from __future__ import annotations + +import logging +import struct + +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 SensorLibrary + +_LOGGER = logging.getLogger(__name__) + + +def address_to_bytes(address: str) -> bytes: + """Return the address as bytes.""" + return bytes([int(x, 16) for x in address.split(":")]) + + +UNPACK_DATA = struct.Struct(">BHHHB").unpack + +_DEVICE_TYPE_FROM_MODEL = { + 0x20: "TZC4", + 0x21: "TZC4", + 0x24: "QJ-J", + 0x25: "QJ-J", + 0x30: "TZC4", + 0x31: "TZC4", +} + +LBS_TO_KGS = 0.45359237 + + +class LeaoneBluetoothDeviceData(BluetoothData): + """Data for Leaone BLE sensors.""" + + def _start_update(self, service_info: BluetoothServiceInfo) -> None: + """Update from BLE advertisement data.""" + _LOGGER.debug("Parsing Leaone BLE advertisement data: %s", service_info) + if ( + not service_info.manufacturer_data + or service_info.service_data + or service_info.service_uuids + ): + return + address = service_info.address + last_id = list(service_info.manufacturer_data)[-1] + last_data = service_info.manufacturer_data[last_id] + if len(last_data) != 13: + return + + mac_trailer = last_data[-6:] + expected_mac = address_to_bytes(service_info.address) + if expected_mac != mac_trailer: + return + + model = last_data[6] + if device_type := _DEVICE_TYPE_FROM_MODEL.get(model): + self.set_device_manufacturer("Leaone") + self.set_device_type(device_type) + name = f"{device_type} {short_address(address)}" + self.set_title(name) + self.set_device_name(name) + else: + return + + changed_manufacturer_data = self.changed_manufacturer_data(service_info) + if not changed_manufacturer_data or len(changed_manufacturer_data) > 1: + # If len(changed_manufacturer_data) > 1 it means we switched + # ble adapters so we do not know which data is the latest + # and we need to wait for the next update. + return + + last_id = list(changed_manufacturer_data)[-1] + data = ( + int(last_id).to_bytes(2, byteorder="little") + + changed_manufacturer_data[last_id] + ) + xvalue = data[1:9] + (frame_cnt, weight, impedance, control, stabilized_byte) = UNPACK_DATA(xvalue) + self.set_precision(2) + packet_id = frame_cnt << 8 | stabilized_byte + self.update_predefined_sensor(SensorLibrary.PACKET_ID__NONE, packet_id) + if stabilized_byte in (0x20,): # KGS + self.update_predefined_sensor( + SensorLibrary.MASS_NON_STABILIZED__MASS_KILOGRAMS, + weight / 10, + "non_stabilized_mass", + ) + elif stabilized_byte in (0x21,): # KGS + self.update_predefined_sensor( + SensorLibrary.MASS__MASS_KILOGRAMS, weight / 10 + ) + self.update_predefined_sensor(SensorLibrary.IMPEDANCE__OHM, impedance / 10) + elif stabilized_byte in (0x30,): # LBS + self.update_predefined_sensor( + SensorLibrary.MASS_NON_STABILIZED__MASS_KILOGRAMS, + (weight / 10) * LBS_TO_KGS, + "non_stabilized_mass", + ) + elif stabilized_byte in (0x31,): # LBS + self.update_predefined_sensor( + SensorLibrary.MASS__MASS_KILOGRAMS, + (weight / 10) * LBS_TO_KGS, + ) + self.update_predefined_sensor(SensorLibrary.IMPEDANCE__OHM, impedance / 10) + elif stabilized_byte in (0x24,): # KGS + self.update_predefined_sensor( + SensorLibrary.MASS_NON_STABILIZED__MASS_KILOGRAMS, + weight / 100, + "non_stabilized_mass", + ) + elif stabilized_byte in (0x25,): # KGS + self.update_predefined_sensor( + SensorLibrary.MASS__MASS_KILOGRAMS, weight / 100 + ) + self.update_predefined_sensor(SensorLibrary.IMPEDANCE__OHM, impedance / 10) diff --git a/tests/test_main.py b/tests/test_main.py deleted file mode 100644 index 11d1602..0000000 --- a/tests/test_main.py +++ /dev/null @@ -1,6 +0,0 @@ -from leaone_ble.main import add - - -def test_add(): - """Adding two number works as expected.""" - assert add(1, 1) == 2 diff --git a/tests/test_parser.py b/tests/test_parser.py new file mode 100644 index 0000000..2044a0f --- /dev/null +++ b/tests/test_parser.py @@ -0,0 +1,373 @@ +from bluetooth_sensor_state_data import BluetoothServiceInfo, SensorUpdate +from sensor_state_data import ( + DeviceKey, + SensorDescription, + SensorDeviceClass, + SensorDeviceInfo, + SensorValue, + Units, +) + +from leaone_ble.parser import LeaoneBluetoothDeviceData + +SCALE_SERVICE_INFO = BluetoothServiceInfo( + name="", + address="5F:5A:5C:52:D3:94", + rssi=-63, + manufacturer_data={57280: b"\x06\xa4\x00\x00\x00\x020_Z\\R\xd3\x94"}, + service_uuids=[], + service_data={}, + source="local", +) +SCALE_SERVICE_INFO_2 = BluetoothServiceInfo( + name="", + address="5F:5A:5C:52:D3:94", + rssi=-63, + manufacturer_data={ + 57280: b"\x06\xa4\x00\x00\x00\x020_Z\\R\xd3\x94", + 63424: b"\x06\xa4\x13\x80\x00\x021_Z\\R\xd3\x94", + }, + service_uuids=[], + service_data={}, + source="local", +) +SCALE_SERVICE_INFO_3 = BluetoothServiceInfo( + name="", + address="5F:5A:5C:52:D3:94", + rssi=-63, + manufacturer_data={ + 57280: b"\x06\xa4\x00\x00\x00\x020_Z\\R\xd3\x94", + 63424: b"\x06\xa4\x13\x80\x00\x021_Z\\R\xd3\x94", + 6592: b"\x06\x8e\x00\x00\x00\x020_Z\\R\xd3\x94", + }, + service_uuids=[], + service_data={}, + source="local", +) + +SCALE_SERVICE_INFO_KGS = BluetoothServiceInfo( + name="", + address="5F:5A:5C:52:D3:94", + rssi=-63, + manufacturer_data={22976: b"\x00\x00\x00\x00\x00\x02 _Z\\R\xd3\x94"}, + service_uuids=[], + service_data={}, + source="local", +) + +SCALE_SERVICE_INFO_KGS_2 = BluetoothServiceInfo( + name="", + address="5F:5A:5C:52:D3:94", + rssi=-63, + manufacturer_data={ + 22976: b"\x00\x00\x00\x00\x00\x02 _Z\\R\xd3\x94", + 27328: b"\x00\x00\x00\x00\x00\x02 _Z\\R\xd3\x94", + }, + service_uuids=[], + service_data={}, + source="local", +) + + +def test_can_create(): + LeaoneBluetoothDeviceData() + + +def test_scale_lbs(): + parser = LeaoneBluetoothDeviceData() + result = parser.update(SCALE_SERVICE_INFO) + assert result == SensorUpdate( + title="TZC4 D394", + devices={ + None: SensorDeviceInfo( + name="TZC4 D394", + model="TZC4", + manufacturer="Leaone", + sw_version=None, + hw_version=None, + ) + }, + entity_descriptions={ + DeviceKey(key="non_stabilized_mass", device_id=None): SensorDescription( + device_key=DeviceKey(key="non_stabilized_mass", device_id=None), + device_class=SensorDeviceClass.MASS_NON_STABILIZED, + native_unit_of_measurement=Units.MASS_KILOGRAMS, + ), + DeviceKey(key="packet_id", device_id=None): SensorDescription( + device_key=DeviceKey(key="packet_id", device_id=None), + device_class=SensorDeviceClass.PACKET_ID, + native_unit_of_measurement=None, + ), + 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, + ), + }, + entity_values={ + DeviceKey(key="non_stabilized_mass", device_id=None): SensorValue( + device_key=DeviceKey(key="non_stabilized_mass", device_id=None), + name="Non " "Stabilized " "Mass", + native_value=77.11, + ), + DeviceKey(key="packet_id", device_id=None): SensorValue( + device_key=DeviceKey(key="packet_id", device_id=None), + name="Packet " "Id", + native_value=57136, + ), + DeviceKey(key="signal_strength", device_id=None): SensorValue( + device_key=DeviceKey(key="signal_strength", device_id=None), + name="Signal " "Strength", + native_value=-63, + ), + }, + binary_entity_descriptions={}, + binary_entity_values={}, + events={}, + ) + result = parser.update(SCALE_SERVICE_INFO_2) + assert result == SensorUpdate( + title="TZC4 D394", + devices={ + None: SensorDeviceInfo( + name="TZC4 D394", + model="TZC4", + manufacturer="Leaone", + sw_version=None, + hw_version=None, + ) + }, + entity_descriptions={ + DeviceKey(key="non_stabilized_mass", device_id=None): SensorDescription( + device_key=DeviceKey(key="non_stabilized_mass", device_id=None), + device_class=SensorDeviceClass.MASS_NON_STABILIZED, + native_unit_of_measurement=Units.MASS_KILOGRAMS, + ), + DeviceKey(key="packet_id", device_id=None): SensorDescription( + device_key=DeviceKey(key="packet_id", device_id=None), + device_class=SensorDeviceClass.PACKET_ID, + native_unit_of_measurement=None, + ), + 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="mass", device_id=None): SensorDescription( + device_key=DeviceKey(key="mass", device_id=None), + device_class=SensorDeviceClass.MASS, + native_unit_of_measurement=Units.MASS_KILOGRAMS, + ), + DeviceKey(key="impedance", device_id=None): SensorDescription( + device_key=DeviceKey(key="impedance", device_id=None), + device_class=SensorDeviceClass.IMPEDANCE, + native_unit_of_measurement=Units.OHM, + ), + }, + entity_values={ + DeviceKey(key="non_stabilized_mass", device_id=None): SensorValue( + device_key=DeviceKey(key="non_stabilized_mass", device_id=None), + name="Non " "Stabilized " "Mass", + native_value=77.11, + ), + DeviceKey(key="packet_id", device_id=None): SensorValue( + device_key=DeviceKey(key="packet_id", device_id=None), + name="Packet " "Id", + native_value=63281, + ), + 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="mass", device_id=None): SensorValue( + device_key=DeviceKey(key="mass", device_id=None), + name="Mass", + native_value=77.11, + ), + DeviceKey(key="impedance", device_id=None): SensorValue( + device_key=DeviceKey(key="impedance", device_id=None), + name="Impedance", + native_value=499.2, + ), + }, + binary_entity_descriptions={}, + binary_entity_values={}, + events={}, + ) + + service_info = SCALE_SERVICE_INFO_3 + result = parser.update(service_info) + assert result == SensorUpdate( + title="TZC4 D394", + devices={ + None: SensorDeviceInfo( + name="TZC4 D394", + model="TZC4", + manufacturer="Leaone", + sw_version=None, + hw_version=None, + ) + }, + entity_descriptions={ + DeviceKey(key="packet_id", device_id=None): SensorDescription( + device_key=DeviceKey(key="packet_id", device_id=None), + device_class=SensorDeviceClass.PACKET_ID, + native_unit_of_measurement=None, + ), + 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="non_stabilized_mass", device_id=None): SensorDescription( + device_key=DeviceKey(key="non_stabilized_mass", device_id=None), + device_class=SensorDeviceClass.MASS_NON_STABILIZED, + native_unit_of_measurement=Units.MASS_KILOGRAMS, + ), + DeviceKey(key="impedance", device_id=None): SensorDescription( + device_key=DeviceKey(key="impedance", device_id=None), + device_class=SensorDeviceClass.IMPEDANCE, + native_unit_of_measurement=Units.OHM, + ), + DeviceKey(key="mass", device_id=None): SensorDescription( + device_key=DeviceKey(key="mass", device_id=None), + device_class=SensorDeviceClass.MASS, + native_unit_of_measurement=Units.MASS_KILOGRAMS, + ), + }, + entity_values={ + DeviceKey(key="packet_id", device_id=None): SensorValue( + device_key=DeviceKey(key="packet_id", device_id=None), + name="Packet " "Id", + native_value=6448, + ), + 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="non_stabilized_mass", device_id=None): SensorValue( + device_key=DeviceKey(key="non_stabilized_mass", device_id=None), + name="Non " "Stabilized " "Mass", + native_value=76.11, + ), + DeviceKey(key="impedance", device_id=None): SensorValue( + device_key=DeviceKey(key="impedance", device_id=None), + name="Impedance", + native_value=499.2, + ), + DeviceKey(key="mass", device_id=None): SensorValue( + device_key=DeviceKey(key="mass", device_id=None), + name="Mass", + native_value=77.11, + ), + }, + binary_entity_descriptions={}, + binary_entity_values={}, + events={}, + ) + + +def test_scale_kgs(): + parser = LeaoneBluetoothDeviceData() + result = parser.update(SCALE_SERVICE_INFO_KGS) + assert result == SensorUpdate( + title="TZC4 D394", + devices={ + None: SensorDeviceInfo( + name="TZC4 D394", + model="TZC4", + manufacturer="Leaone", + sw_version=None, + hw_version=None, + ) + }, + entity_descriptions={ + DeviceKey(key="non_stabilized_mass", device_id=None): SensorDescription( + device_key=DeviceKey(key="non_stabilized_mass", device_id=None), + device_class=SensorDeviceClass.MASS_NON_STABILIZED, + native_unit_of_measurement=Units.MASS_KILOGRAMS, + ), + DeviceKey(key="packet_id", device_id=None): SensorDescription( + device_key=DeviceKey(key="packet_id", device_id=None), + device_class=SensorDeviceClass.PACKET_ID, + native_unit_of_measurement=None, + ), + 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, + ), + }, + entity_values={ + DeviceKey(key="non_stabilized_mass", device_id=None): SensorValue( + device_key=DeviceKey(key="non_stabilized_mass", device_id=None), + name="Non " "Stabilized " "Mass", + native_value=0.0, + ), + DeviceKey(key="packet_id", device_id=None): SensorValue( + device_key=DeviceKey(key="packet_id", device_id=None), + name="Packet " "Id", + native_value=22816, + ), + DeviceKey(key="signal_strength", device_id=None): SensorValue( + device_key=DeviceKey(key="signal_strength", device_id=None), + name="Signal " "Strength", + native_value=-63, + ), + }, + binary_entity_descriptions={}, + binary_entity_values={}, + events={}, + ) + result = parser.update(SCALE_SERVICE_INFO_KGS_2) + assert result == SensorUpdate( + title="TZC4 D394", + devices={ + None: SensorDeviceInfo( + name="TZC4 D394", + model="TZC4", + manufacturer="Leaone", + sw_version=None, + hw_version=None, + ) + }, + entity_descriptions={ + DeviceKey(key="non_stabilized_mass", device_id=None): SensorDescription( + device_key=DeviceKey(key="non_stabilized_mass", device_id=None), + device_class=SensorDeviceClass.MASS_NON_STABILIZED, + native_unit_of_measurement=Units.MASS_KILOGRAMS, + ), + DeviceKey(key="packet_id", device_id=None): SensorDescription( + device_key=DeviceKey(key="packet_id", device_id=None), + device_class=SensorDeviceClass.PACKET_ID, + native_unit_of_measurement=None, + ), + 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, + ), + }, + entity_values={ + DeviceKey(key="non_stabilized_mass", device_id=None): SensorValue( + device_key=DeviceKey(key="non_stabilized_mass", device_id=None), + name="Non " "Stabilized " "Mass", + native_value=0.0, + ), + DeviceKey(key="packet_id", device_id=None): SensorValue( + device_key=DeviceKey(key="packet_id", device_id=None), + name="Packet " "Id", + native_value=27168, + ), + DeviceKey(key="signal_strength", device_id=None): SensorValue( + device_key=DeviceKey(key="signal_strength", device_id=None), + name="Signal " "Strength", + native_value=-63, + ), + }, + binary_entity_descriptions={}, + binary_entity_values={}, + events={}, + )