bleak-esphome 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,22 @@
1
+
2
+ MIT License
3
+
4
+ Copyright (c) 2023 J. Nick Koston
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
@@ -0,0 +1,97 @@
1
+ Metadata-Version: 2.1
2
+ Name: bleak-esphome
3
+ Version: 0.1.0
4
+ Summary: Bleak backend of ESPHome
5
+ Home-page: https://github.com/bluetooth-devices/bleak-esphome
6
+ License: MIT
7
+ Author: J. Nick Koston
8
+ Author-email: bluetooth@koston.org
9
+ Requires-Python: >=3.10,<3.13
10
+ Classifier: Development Status :: 2 - Pre-Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Natural Language :: English
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development :: Libraries
20
+ Requires-Dist: aioesphomeapi (>=21.0.0)
21
+ Requires-Dist: bleak (>=0.21.1)
22
+ Requires-Dist: bluetooth-data-tools (>=1.17.0)
23
+ Requires-Dist: habluetooth (>=1.0.0)
24
+ Project-URL: Bug Tracker, https://github.com/bluetooth-devices/bleak-esphome/issues
25
+ Project-URL: Changelog, https://github.com/bluetooth-devices/bleak-esphome/blob/main/CHANGELOG.md
26
+ Project-URL: Documentation, https://bleak-esphome.readthedocs.io
27
+ Project-URL: Repository, https://github.com/bluetooth-devices/bleak-esphome
28
+ Description-Content-Type: text/markdown
29
+
30
+ # bleak-esphome
31
+
32
+ <p align="center">
33
+ <a href="https://github.com/bluetooth-devices/bleak-esphome/actions/workflows/ci.yml?query=branch%3Amain">
34
+ <img src="https://img.shields.io/github/actions/workflow/status/bluetooth-devices/bleak-esphome/ci.yml?branch=main&label=CI&logo=github&style=flat-square" alt="CI Status" >
35
+ </a>
36
+ <a href="https://bleak-esphome.readthedocs.io">
37
+ <img src="https://img.shields.io/readthedocs/bleak-esphome.svg?logo=read-the-docs&logoColor=fff&style=flat-square" alt="Documentation Status">
38
+ </a>
39
+ <a href="https://codecov.io/gh/bluetooth-devices/bleak-esphome">
40
+ <img src="https://img.shields.io/codecov/c/github/bluetooth-devices/bleak-esphome.svg?logo=codecov&logoColor=fff&style=flat-square" alt="Test coverage percentage">
41
+ </a>
42
+ </p>
43
+ <p align="center">
44
+ <a href="https://python-poetry.org/">
45
+ <img src="https://img.shields.io/badge/packaging-poetry-299bd7?style=flat-square&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAASCAYAAABrXO8xAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAJJSURBVHgBfZLPa1NBEMe/s7tNXoxW1KJQKaUHkXhQvHgW6UHQQ09CBS/6V3hKc/AP8CqCrUcpmop3Cx48eDB4yEECjVQrlZb80CRN8t6OM/teagVxYZi38+Yz853dJbzoMV3MM8cJUcLMSUKIE8AzQ2PieZzFxEJOHMOgMQQ+dUgSAckNXhapU/NMhDSWLs1B24A8sO1xrN4NECkcAC9ASkiIJc6k5TRiUDPhnyMMdhKc+Zx19l6SgyeW76BEONY9exVQMzKExGKwwPsCzza7KGSSWRWEQhyEaDXp6ZHEr416ygbiKYOd7TEWvvcQIeusHYMJGhTwF9y7sGnSwaWyFAiyoxzqW0PM/RjghPxF2pWReAowTEXnDh0xgcLs8l2YQmOrj3N7ByiqEoH0cARs4u78WgAVkoEDIDoOi3AkcLOHU60RIg5wC4ZuTC7FaHKQm8Hq1fQuSOBvX/sodmNJSB5geaF5CPIkUeecdMxieoRO5jz9bheL6/tXjrwCyX/UYBUcjCaWHljx1xiX6z9xEjkYAzbGVnB8pvLmyXm9ep+W8CmsSHQQY77Zx1zboxAV0w7ybMhQmfqdmmw3nEp1I0Z+FGO6M8LZdoyZnuzzBdjISicKRnpxzI9fPb+0oYXsNdyi+d3h9bm9MWYHFtPeIZfLwzmFDKy1ai3p+PDls1Llz4yyFpferxjnyjJDSEy9CaCx5m2cJPerq6Xm34eTrZt3PqxYO1XOwDYZrFlH1fWnpU38Y9HRze3lj0vOujZcXKuuXm3jP+s3KbZVra7y2EAAAAAASUVORK5CYII=" alt="Poetry">
46
+ </a>
47
+ <a href="https://github.com/ambv/black">
48
+ <img src="https://img.shields.io/badge/code%20style-black-000000.svg?style=flat-square" alt="black">
49
+ </a>
50
+ <a href="https://github.com/pre-commit/pre-commit">
51
+ <img src="https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=flat-square" alt="pre-commit">
52
+ </a>
53
+ </p>
54
+ <p align="center">
55
+ <a href="https://pypi.org/project/bleak-esphome/">
56
+ <img src="https://img.shields.io/pypi/v/bleak-esphome.svg?logo=python&logoColor=fff&style=flat-square" alt="PyPI Version">
57
+ </a>
58
+ <img src="https://img.shields.io/pypi/pyversions/bleak-esphome.svg?style=flat-square&logo=python&amp;logoColor=fff" alt="Supported Python versions">
59
+ <img src="https://img.shields.io/pypi/l/bleak-esphome.svg?style=flat-square" alt="License">
60
+ </p>
61
+
62
+ ---
63
+
64
+ **Documentation**: <a href="https://bleak-esphome.readthedocs.io" target="_blank">https://bleak-esphome.readthedocs.io </a>
65
+
66
+ **Source Code**: <a href="https://github.com/bluetooth-devices/bleak-esphome" target="_blank">https://github.com/bluetooth-devices/bleak-esphome </a>
67
+
68
+ ---
69
+
70
+ Bleak backend of ESPHome
71
+
72
+ ## Installation
73
+
74
+ Install this via pip (or your favourite package manager):
75
+
76
+ `pip install bleak-esphome`
77
+
78
+ ## Contributors ✨
79
+
80
+ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
81
+
82
+ <!-- prettier-ignore-start -->
83
+ <!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
84
+ <!-- markdownlint-disable -->
85
+ <!-- markdownlint-enable -->
86
+ <!-- ALL-CONTRIBUTORS-LIST:END -->
87
+ <!-- prettier-ignore-end -->
88
+
89
+ This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
90
+
91
+ ## Credits
92
+
93
+ This package was created with
94
+ [Copier](https://copier.readthedocs.io/) and the
95
+ [browniebroke/pypackage-template](https://github.com/browniebroke/pypackage-template)
96
+ project template.
97
+
@@ -0,0 +1,67 @@
1
+ # bleak-esphome
2
+
3
+ <p align="center">
4
+ <a href="https://github.com/bluetooth-devices/bleak-esphome/actions/workflows/ci.yml?query=branch%3Amain">
5
+ <img src="https://img.shields.io/github/actions/workflow/status/bluetooth-devices/bleak-esphome/ci.yml?branch=main&label=CI&logo=github&style=flat-square" alt="CI Status" >
6
+ </a>
7
+ <a href="https://bleak-esphome.readthedocs.io">
8
+ <img src="https://img.shields.io/readthedocs/bleak-esphome.svg?logo=read-the-docs&logoColor=fff&style=flat-square" alt="Documentation Status">
9
+ </a>
10
+ <a href="https://codecov.io/gh/bluetooth-devices/bleak-esphome">
11
+ <img src="https://img.shields.io/codecov/c/github/bluetooth-devices/bleak-esphome.svg?logo=codecov&logoColor=fff&style=flat-square" alt="Test coverage percentage">
12
+ </a>
13
+ </p>
14
+ <p align="center">
15
+ <a href="https://python-poetry.org/">
16
+ <img src="https://img.shields.io/badge/packaging-poetry-299bd7?style=flat-square&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAASCAYAAABrXO8xAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAJJSURBVHgBfZLPa1NBEMe/s7tNXoxW1KJQKaUHkXhQvHgW6UHQQ09CBS/6V3hKc/AP8CqCrUcpmop3Cx48eDB4yEECjVQrlZb80CRN8t6OM/teagVxYZi38+Yz853dJbzoMV3MM8cJUcLMSUKIE8AzQ2PieZzFxEJOHMOgMQQ+dUgSAckNXhapU/NMhDSWLs1B24A8sO1xrN4NECkcAC9ASkiIJc6k5TRiUDPhnyMMdhKc+Zx19l6SgyeW76BEONY9exVQMzKExGKwwPsCzza7KGSSWRWEQhyEaDXp6ZHEr416ygbiKYOd7TEWvvcQIeusHYMJGhTwF9y7sGnSwaWyFAiyoxzqW0PM/RjghPxF2pWReAowTEXnDh0xgcLs8l2YQmOrj3N7ByiqEoH0cARs4u78WgAVkoEDIDoOi3AkcLOHU60RIg5wC4ZuTC7FaHKQm8Hq1fQuSOBvX/sodmNJSB5geaF5CPIkUeecdMxieoRO5jz9bheL6/tXjrwCyX/UYBUcjCaWHljx1xiX6z9xEjkYAzbGVnB8pvLmyXm9ep+W8CmsSHQQY77Zx1zboxAV0w7ybMhQmfqdmmw3nEp1I0Z+FGO6M8LZdoyZnuzzBdjISicKRnpxzI9fPb+0oYXsNdyi+d3h9bm9MWYHFtPeIZfLwzmFDKy1ai3p+PDls1Llz4yyFpferxjnyjJDSEy9CaCx5m2cJPerq6Xm34eTrZt3PqxYO1XOwDYZrFlH1fWnpU38Y9HRze3lj0vOujZcXKuuXm3jP+s3KbZVra7y2EAAAAAASUVORK5CYII=" alt="Poetry">
17
+ </a>
18
+ <a href="https://github.com/ambv/black">
19
+ <img src="https://img.shields.io/badge/code%20style-black-000000.svg?style=flat-square" alt="black">
20
+ </a>
21
+ <a href="https://github.com/pre-commit/pre-commit">
22
+ <img src="https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=flat-square" alt="pre-commit">
23
+ </a>
24
+ </p>
25
+ <p align="center">
26
+ <a href="https://pypi.org/project/bleak-esphome/">
27
+ <img src="https://img.shields.io/pypi/v/bleak-esphome.svg?logo=python&logoColor=fff&style=flat-square" alt="PyPI Version">
28
+ </a>
29
+ <img src="https://img.shields.io/pypi/pyversions/bleak-esphome.svg?style=flat-square&logo=python&amp;logoColor=fff" alt="Supported Python versions">
30
+ <img src="https://img.shields.io/pypi/l/bleak-esphome.svg?style=flat-square" alt="License">
31
+ </p>
32
+
33
+ ---
34
+
35
+ **Documentation**: <a href="https://bleak-esphome.readthedocs.io" target="_blank">https://bleak-esphome.readthedocs.io </a>
36
+
37
+ **Source Code**: <a href="https://github.com/bluetooth-devices/bleak-esphome" target="_blank">https://github.com/bluetooth-devices/bleak-esphome </a>
38
+
39
+ ---
40
+
41
+ Bleak backend of ESPHome
42
+
43
+ ## Installation
44
+
45
+ Install this via pip (or your favourite package manager):
46
+
47
+ `pip install bleak-esphome`
48
+
49
+ ## Contributors ✨
50
+
51
+ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
52
+
53
+ <!-- prettier-ignore-start -->
54
+ <!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
55
+ <!-- markdownlint-disable -->
56
+ <!-- markdownlint-enable -->
57
+ <!-- ALL-CONTRIBUTORS-LIST:END -->
58
+ <!-- prettier-ignore-end -->
59
+
60
+ This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
61
+
62
+ ## Credits
63
+
64
+ This package was created with
65
+ [Copier](https://copier.readthedocs.io/) and the
66
+ [browniebroke/pypackage-template](https://github.com/browniebroke/pypackage-template)
67
+ project template.
@@ -0,0 +1,150 @@
1
+ [tool.poetry]
2
+ name = "bleak-esphome"
3
+ version = "0.1.0"
4
+ description = "Bleak backend of ESPHome"
5
+ authors = ["J. Nick Koston <bluetooth@koston.org>"]
6
+ license = "MIT"
7
+ readme = "README.md"
8
+ repository = "https://github.com/bluetooth-devices/bleak-esphome"
9
+ documentation = "https://bleak-esphome.readthedocs.io"
10
+ classifiers = [
11
+ "Development Status :: 2 - Pre-Alpha",
12
+ "Intended Audience :: Developers",
13
+ "Natural Language :: English",
14
+ "Operating System :: OS Independent",
15
+ "Topic :: Software Development :: Libraries",
16
+ ]
17
+ packages = [
18
+ { include = "bleak_esphome", from = "src" },
19
+ ]
20
+
21
+ [tool.poetry.urls]
22
+ "Bug Tracker" = "https://github.com/bluetooth-devices/bleak-esphome/issues"
23
+ "Changelog" = "https://github.com/bluetooth-devices/bleak-esphome/blob/main/CHANGELOG.md"
24
+
25
+ [tool.poetry.dependencies]
26
+ python = ">=3.10,<3.13"
27
+ aioesphomeapi = ">=21.0.0"
28
+ bleak = ">=0.21.1"
29
+ bluetooth-data-tools = ">=1.17.0"
30
+ habluetooth = ">=1.0.0"
31
+
32
+ [tool.poetry.group.dev.dependencies]
33
+ pytest = "^7.0"
34
+ pytest-cov = "^3.0"
35
+
36
+ [tool.poetry.group.docs]
37
+ optional = true
38
+
39
+ [tool.poetry.group.docs.dependencies]
40
+ myst-parser = ">=0.16"
41
+ sphinx = ">=4.0"
42
+ furo = ">=2023.5.20"
43
+ sphinx-autobuild = ">=2021.3.14"
44
+
45
+ [tool.semantic_release]
46
+ version_toml = ["pyproject.toml:tool.poetry.version"]
47
+ version_variables = [
48
+ "src/bleak_esphome/__init__.py:__version__",
49
+ "docs/conf.py:release",
50
+ ]
51
+ build_command = "pip install poetry && poetry build"
52
+
53
+ [tool.semantic_release.changelog]
54
+ exclude_commit_patterns = [
55
+ "chore*",
56
+ "ci*",
57
+ ]
58
+
59
+ [tool.semantic_release.changelog.environment]
60
+ keep_trailing_newline = true
61
+
62
+ [tool.semantic_release.branches.main]
63
+ match = "main"
64
+
65
+ [tool.semantic_release.branches.noop]
66
+ match = "(?!main$)"
67
+ prerelease = true
68
+
69
+ [tool.pytest.ini_options]
70
+ addopts = "-v -Wdefault --cov=bleak_esphome --cov-report=term-missing:skip-covered"
71
+ pythonpath = ["src"]
72
+
73
+ [tool.coverage.run]
74
+ branch = true
75
+
76
+ [tool.coverage.report]
77
+ exclude_lines = [
78
+ "pragma: no cover",
79
+ "@overload",
80
+ "if TYPE_CHECKING",
81
+ "raise NotImplementedError",
82
+ 'if __name__ == "__main__":',
83
+ ]
84
+
85
+ [tool.ruff]
86
+ target-version = "py38"
87
+ line-length = 88
88
+ ignore = [
89
+ "D203", # 1 blank line required before class docstring
90
+ "D212", # Multi-line docstring summary should start at the first line
91
+ "D100", # Missing docstring in public module
92
+ "D104", # Missing docstring in public package
93
+ "D107", # Missing docstring in `__init__`
94
+ "D401", # First line of docstring should be in imperative mood
95
+ ]
96
+ select = [
97
+ "B", # flake8-bugbear
98
+ "D", # flake8-docstrings
99
+ "C4", # flake8-comprehensions
100
+ "S", # flake8-bandit
101
+ "F", # pyflake
102
+ "E", # pycodestyle
103
+ "W", # pycodestyle
104
+ "UP", # pyupgrade
105
+ "I", # isort
106
+ "RUF", # ruff specific
107
+ ]
108
+
109
+ [tool.ruff.per-file-ignores]
110
+ "tests/**/*" = [
111
+ "D100",
112
+ "D101",
113
+ "D102",
114
+ "D103",
115
+ "D104",
116
+ "S101",
117
+ ]
118
+ "setup.py" = ["D100"]
119
+ "conftest.py" = ["D100"]
120
+ "docs/conf.py" = ["D100"]
121
+
122
+ [tool.ruff.isort]
123
+ known-first-party = ["bleak_esphome", "tests"]
124
+
125
+ [tool.mypy]
126
+ check_untyped_defs = true
127
+ disallow_any_generics = true
128
+ disallow_incomplete_defs = true
129
+ disallow_untyped_defs = true
130
+ mypy_path = "src/"
131
+ no_implicit_optional = true
132
+ show_error_codes = true
133
+ warn_unreachable = true
134
+ warn_unused_ignores = true
135
+ exclude = [
136
+ 'docs/.*',
137
+ 'setup.py',
138
+ ]
139
+
140
+ [[tool.mypy.overrides]]
141
+ module = "tests.*"
142
+ allow_untyped_defs = true
143
+
144
+ [[tool.mypy.overrides]]
145
+ module = "docs.*"
146
+ ignore_errors = true
147
+
148
+ [build-system]
149
+ requires = ["poetry-core>=1.0.0"]
150
+ build-backend = "poetry.core.masonry.api"
File without changes
@@ -0,0 +1,50 @@
1
+ """Bluetooth cache for esphome."""
2
+ from __future__ import annotations
3
+
4
+ from collections.abc import MutableMapping
5
+ from dataclasses import dataclass, field
6
+
7
+ from bleak.backends.service import BleakGATTServiceCollection
8
+ from lru import LRU # pylint: disable=no-name-in-module
9
+
10
+ MAX_CACHED_SERVICES = 128
11
+
12
+
13
+ @dataclass(slots=True)
14
+ class ESPHomeBluetoothCache:
15
+ """Shared cache between all ESPHome bluetooth devices."""
16
+
17
+ _gatt_services_cache: MutableMapping[int, BleakGATTServiceCollection] = field(
18
+ default_factory=lambda: LRU(MAX_CACHED_SERVICES)
19
+ )
20
+ _gatt_mtu_cache: MutableMapping[int, int] = field(
21
+ default_factory=lambda: LRU(MAX_CACHED_SERVICES)
22
+ )
23
+
24
+ def get_gatt_services_cache(
25
+ self, address: int
26
+ ) -> BleakGATTServiceCollection | None:
27
+ """Get the BleakGATTServiceCollection for the given address."""
28
+ return self._gatt_services_cache.get(address)
29
+
30
+ def set_gatt_services_cache(
31
+ self, address: int, services: BleakGATTServiceCollection
32
+ ) -> None:
33
+ """Set the BleakGATTServiceCollection for the given address."""
34
+ self._gatt_services_cache[address] = services
35
+
36
+ def clear_gatt_services_cache(self, address: int) -> None:
37
+ """Clear the BleakGATTServiceCollection for the given address."""
38
+ self._gatt_services_cache.pop(address, None)
39
+
40
+ def get_gatt_mtu_cache(self, address: int) -> int | None:
41
+ """Get the mtu cache for the given address."""
42
+ return self._gatt_mtu_cache.get(address)
43
+
44
+ def set_gatt_mtu_cache(self, address: int, mtu: int) -> None:
45
+ """Set the mtu cache for the given address."""
46
+ self._gatt_mtu_cache[address] = mtu
47
+
48
+ def clear_gatt_mtu_cache(self, address: int) -> None:
49
+ """Clear the mtu cache for the given address."""
50
+ self._gatt_mtu_cache.pop(address, None)
@@ -0,0 +1,96 @@
1
+ """BleakGATTCharacteristicESPHome."""
2
+ from __future__ import annotations
3
+
4
+ import contextlib
5
+ from uuid import UUID
6
+
7
+ from aioesphomeapi.model import BluetoothGATTCharacteristic
8
+ from bleak.backends.characteristic import BleakGATTCharacteristic
9
+ from bleak.backends.descriptor import BleakGATTDescriptor
10
+
11
+ PROPERTY_MASKS = {
12
+ 2**n: prop
13
+ for n, prop in enumerate(
14
+ (
15
+ "broadcast",
16
+ "read",
17
+ "write-without-response",
18
+ "write",
19
+ "notify",
20
+ "indicate",
21
+ "authenticated-signed-writes",
22
+ "extended-properties",
23
+ "reliable-writes",
24
+ "writable-auxiliaries",
25
+ )
26
+ )
27
+ }
28
+
29
+
30
+ class BleakGATTCharacteristicESPHome(BleakGATTCharacteristic):
31
+ """GATT Characteristic implementation for the ESPHome backend."""
32
+
33
+ obj: BluetoothGATTCharacteristic
34
+
35
+ def __init__(
36
+ self,
37
+ obj: BluetoothGATTCharacteristic,
38
+ max_write_without_response_size: int,
39
+ service_uuid: str,
40
+ service_handle: int,
41
+ ) -> None:
42
+ """Init a BleakGATTCharacteristicESPHome."""
43
+ super().__init__(obj, max_write_without_response_size)
44
+ self.__descriptors: list[BleakGATTDescriptor] = []
45
+ self.__service_uuid: str = service_uuid
46
+ self.__service_handle: int = service_handle
47
+ char_props = self.obj.properties
48
+ self.__props: list[str] = [
49
+ prop for mask, prop in PROPERTY_MASKS.items() if char_props & mask
50
+ ]
51
+
52
+ @property
53
+ def service_uuid(self) -> str:
54
+ """Uuid of the Service containing this characteristic."""
55
+ return self.__service_uuid
56
+
57
+ @property
58
+ def service_handle(self) -> int:
59
+ """Integer handle of the Service containing this characteristic."""
60
+ return self.__service_handle
61
+
62
+ @property
63
+ def handle(self) -> int:
64
+ """Integer handle for this characteristic."""
65
+ return self.obj.handle
66
+
67
+ @property
68
+ def uuid(self) -> str:
69
+ """Uuid of this characteristic."""
70
+ return self.obj.uuid
71
+
72
+ @property
73
+ def properties(self) -> list[str]:
74
+ """Properties of this characteristic."""
75
+ return self.__props
76
+
77
+ @property
78
+ def descriptors(self) -> list[BleakGATTDescriptor]:
79
+ """List of descriptors for this service."""
80
+ return self.__descriptors
81
+
82
+ def get_descriptor(self, specifier: int | str | UUID) -> BleakGATTDescriptor | None:
83
+ """Get a descriptor by handle (int) or UUID (str or uuid.UUID)."""
84
+ with contextlib.suppress(StopIteration):
85
+ if isinstance(specifier, int):
86
+ return next(filter(lambda x: x.handle == specifier, self.descriptors))
87
+ return next(filter(lambda x: x.uuid == str(specifier), self.descriptors))
88
+ return None
89
+
90
+ def add_descriptor(self, descriptor: BleakGATTDescriptor) -> None:
91
+ """
92
+ Add a :py:class:`~BleakGATTDescriptor` to the characteristic.
93
+
94
+ Should not be used by end user, but rather by `bleak` itself.
95
+ """
96
+ self.__descriptors.append(descriptor)