Merge pull request #32 from dhalbert/doc-and-cleanup
doc and cleanup pass
This commit is contained in:
commit
e6679abe54
21 changed files with 430 additions and 186 deletions
|
|
@ -31,12 +31,12 @@ Usage Example
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
from adafruit_ble import SmartAdapter
|
||||
from adafruit_ble import BLERadio
|
||||
|
||||
adapter = SmartAdapter()
|
||||
radio = BLERadio()
|
||||
print("scanning")
|
||||
found = set()
|
||||
for entry in adapter.start_scan(timeout=60, minimum_rssi=-80):
|
||||
for entry in radio.start_scan(timeout=60, minimum_rssi=-80):
|
||||
addr = entry.address
|
||||
if addr not in found:
|
||||
print(entry)
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ Implementation Notes
|
|||
**Hardware:**
|
||||
|
||||
Adafruit Feather nRF52840 Express <https://www.adafruit.com/product/4062>
|
||||
Adafruit Circuit Playground Bluefruit <https://www.adafruit.com/product/4333>
|
||||
|
||||
**Software and Dependencies:**
|
||||
|
||||
|
|
@ -53,46 +54,61 @@ __version__ = "0.0.0-auto.0"
|
|||
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
|
||||
|
||||
class BLEConnection:
|
||||
"""This represents a connection to a peer BLE device.
|
||||
|
||||
It acts as a map from a Service type to a Service instance for the connection.
|
||||
"""
|
||||
def __init__(self, connection):
|
||||
self._connection = connection
|
||||
self._discovered_services = {}
|
||||
"""These are the bare remote services from _bleio."""
|
||||
Represents a connection to a peer BLE device.
|
||||
It acts as a map from a `Service` type to a `Service` instance for the connection.
|
||||
|
||||
:param bleio_connection _bleio.Connection: the native `_bleio.Connection` object to wrap
|
||||
|
||||
"""
|
||||
def __init__(self, bleio_connection):
|
||||
self._bleio_connection = bleio_connection
|
||||
# _bleio.Service objects representing services found during discovery.
|
||||
self._discovered_bleio_services = {}
|
||||
# Service objects that wrap remote services.
|
||||
self._constructed_services = {}
|
||||
"""These are the Service instances from the library that wrap the remote services."""
|
||||
|
||||
def _discover_remote(self, uuid):
|
||||
remote_service = None
|
||||
if uuid in self._discovered_services:
|
||||
remote_service = self._discovered_services[uuid]
|
||||
if uuid in self._discovered_bleio_services:
|
||||
remote_service = self._discovered_bleio_services[uuid]
|
||||
else:
|
||||
results = self._connection.discover_remote_services((uuid.bleio_uuid,))
|
||||
results = self._bleio_connection.discover_remote_services((uuid.bleio_uuid,))
|
||||
if results:
|
||||
remote_service = results[0]
|
||||
self._discovered_services[uuid] = remote_service
|
||||
self._discovered_bleio_services[uuid] = remote_service
|
||||
return remote_service
|
||||
|
||||
def __contains__(self, key):
|
||||
"""
|
||||
Allows easy testing for a particular Service class or a particular UUID
|
||||
associated with this connection.
|
||||
|
||||
Example::
|
||||
|
||||
if UARTService in connection:
|
||||
# do something
|
||||
|
||||
if StandardUUID(0x1234) in connection:
|
||||
# do something
|
||||
"""
|
||||
uuid = key
|
||||
if hasattr(key, "uuid"):
|
||||
uuid = key.uuid
|
||||
return self._discover_remote(uuid) is not None
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""Return the Service for the given Service class or uuid, if any."""
|
||||
uuid = key
|
||||
maybe_service = False
|
||||
if hasattr(key, "uuid"):
|
||||
uuid = key.uuid
|
||||
maybe_service = True
|
||||
|
||||
remote_service = self._discover_remote(uuid)
|
||||
|
||||
if uuid in self._constructed_services:
|
||||
return self._constructed_services[uuid]
|
||||
|
||||
remote_service = self._discover_remote(uuid)
|
||||
if remote_service:
|
||||
constructed_service = None
|
||||
if maybe_service:
|
||||
|
|
@ -105,16 +121,19 @@ class BLEConnection:
|
|||
@property
|
||||
def connected(self):
|
||||
"""True if the connection to the peer is still active."""
|
||||
return self._connection.connected
|
||||
return self._bleio_connection.connected
|
||||
|
||||
def disconnect(self):
|
||||
"""Disconnect from peer."""
|
||||
self._connection.disconnect()
|
||||
self._bleio_connection.disconnect()
|
||||
|
||||
class BLERadio:
|
||||
"""The BLERadio class enhances the normal `_bleio.Adapter`.
|
||||
"""
|
||||
BLERadio provides the interfaces for BLE advertising,
|
||||
scanning for advertisements, and connecting to peers. There may be
|
||||
multiple connections active at once.
|
||||
|
||||
It uses the library's `Advertisement` classes and the `BLEConnection` class."""
|
||||
It uses this library's `Advertisement` classes and the `BLEConnection` class."""
|
||||
|
||||
def __init__(self, adapter=None):
|
||||
if not adapter:
|
||||
|
|
@ -123,34 +142,63 @@ class BLERadio:
|
|||
self._current_advertisement = None
|
||||
self._connection_cache = {}
|
||||
|
||||
def start_advertising(self, advertisement, scan_response=None, **kwargs):
|
||||
"""Starts advertising the given advertisement.
|
||||
def start_advertising(self, advertisement, scan_response=None, interval=0.1):
|
||||
"""
|
||||
Starts advertising the given advertisement.
|
||||
|
||||
It takes most kwargs of `_bleio.Adapter.start_advertising`."""
|
||||
:param buf scan_response: scan response data packet bytes.
|
||||
``None`` if no scan response is needed.
|
||||
:param float interval: advertising interval, in seconds
|
||||
"""
|
||||
scan_response_data = None
|
||||
if scan_response:
|
||||
scan_response_data = bytes(scan_response)
|
||||
self._adapter.start_advertising(bytes(advertisement),
|
||||
scan_response=scan_response_data,
|
||||
connectable=advertisement.connectable,
|
||||
**kwargs)
|
||||
interval=interval)
|
||||
|
||||
def stop_advertising(self):
|
||||
"""Stops advertising."""
|
||||
self._adapter.stop_advertising()
|
||||
|
||||
def start_scan(self, *advertisement_types, **kwargs):
|
||||
"""Starts scanning. Returns an iterator of advertisement objects of the types given in
|
||||
advertisement_types. The iterator will block until an advertisement is heard or the scan
|
||||
times out.
|
||||
def start_scan(self, *advertisement_types, buffer_size=512, extended=False, timeout=None,
|
||||
interval=0.1, window=0.1, minimum_rssi=-80, active=True):
|
||||
"""
|
||||
Starts scanning. Returns an iterator of advertisement objects of the types given in
|
||||
advertisement_types. The iterator will block until an advertisement is heard or the scan
|
||||
times out.
|
||||
|
||||
If any ``advertisement_types`` are given, only Advertisements of those types are produced
|
||||
by the returned iterator. If none are given then `Advertisement` objects will be
|
||||
returned."""
|
||||
If any ``advertisement_types`` are given, only Advertisements of those types are produced
|
||||
by the returned iterator. If none are given then `Advertisement` objects will be
|
||||
returned.
|
||||
|
||||
Advertisements and scan responses are filtered and returned separately.
|
||||
|
||||
:param int buffer_size: the maximum number of advertising bytes to buffer.
|
||||
:param bool extended: When True, support extended advertising packets.
|
||||
Increasing buffer_size is recommended when this is set.
|
||||
:param float timeout: the scan timeout in seconds.
|
||||
If None, will scan until `stop_scan` is called.
|
||||
:param float interval: the interval (in seconds) between the start
|
||||
of two consecutive scan windows
|
||||
Must be in the range 0.0025 - 40.959375 seconds.
|
||||
:param float window: the duration (in seconds) to scan a single BLE channel.
|
||||
window must be <= interval.
|
||||
:param int minimum_rssi: the minimum rssi of entries to return.
|
||||
:param bool active: request and retrieve scan responses for scannable advertisements.
|
||||
:return: If any ``advertisement_types`` are given,
|
||||
only Advertisements of those types are produced by the returned iterator.
|
||||
If none are given then `Advertisement` objects will be returned.
|
||||
:rtype: iterable
|
||||
"""
|
||||
prefixes = b""
|
||||
if advertisement_types:
|
||||
prefixes = b"".join(adv.prefix for adv in advertisement_types)
|
||||
for entry in self._adapter.start_scan(prefixes=prefixes, **kwargs):
|
||||
for entry in self._adapter.start_scan(prefixes=prefixes, buffer_size=buffer_size,
|
||||
extended=extended, timeout=timeout,
|
||||
interval=interval, window=window,
|
||||
minimum_rssi=minimum_rssi, active=active):
|
||||
adv_type = Advertisement
|
||||
for possible_type in advertisement_types:
|
||||
if possible_type.matches(entry) and issubclass(possible_type, adv_type):
|
||||
|
|
@ -167,7 +215,14 @@ class BLERadio:
|
|||
self._adapter.stop_scan()
|
||||
|
||||
def connect(self, advertisement, *, timeout=4):
|
||||
"""Initiates a `BLEConnection` to the peer that advertised the given advertisement."""
|
||||
"""
|
||||
Initiates a `BLEConnection` to the peer that advertised the given advertisement.
|
||||
|
||||
:param advertisement Advertisement: An `Advertisement` or a subclass of `Advertisement`
|
||||
:param timeout float: how long to wait for a connection
|
||||
:return: the connection to the peer
|
||||
:rtype: BLEConnection
|
||||
"""
|
||||
connection = self._adapter.connect(advertisement.address, timeout=timeout)
|
||||
self._connection_cache[connection] = BLEConnection(connection)
|
||||
return self._connection_cache[connection]
|
||||
|
|
|
|||
|
|
@ -25,15 +25,13 @@ Advertising is the first phase of BLE where devices can broadcast
|
|||
|
||||
import struct
|
||||
|
||||
def to_hex(b):
|
||||
def to_hex(seq):
|
||||
"""Pretty prints a byte sequence as hex values."""
|
||||
# pylint: disable=invalid-name
|
||||
return " ".join(["{:02x}".format(v) for v in b])
|
||||
return " ".join("{:02x}".format(v) for v in seq)
|
||||
|
||||
def to_bytes_literal(b):
|
||||
def to_bytes_literal(seq):
|
||||
"""Prints a byte sequence as a Python bytes literal that only uses hex encoding."""
|
||||
# pylint: disable=invalid-name
|
||||
return "b\"" + "".join(["\\x{:02x}".format(v) for v in b]) + "\""
|
||||
return "b\"" + "".join("\\x{:02x}".format(v) for v in seq) + "\""
|
||||
|
||||
def decode_data(data, *, key_encoding="B"):
|
||||
"""Helper which decodes length encoded structures into a dictionary with the given key
|
||||
|
|
|
|||
|
|
@ -64,7 +64,8 @@ class BoundServiceList:
|
|||
return uuid in self._vendor_services or uuid in self._standard_services
|
||||
|
||||
def _update(self, adt, uuids):
|
||||
if len(uuids) == 0:
|
||||
if not uuids:
|
||||
# uuids is empty
|
||||
del self._advertisement.data_dict[adt]
|
||||
uuid_length = uuids[0].size // 8
|
||||
b = bytearray(len(uuids) * uuid_length)
|
||||
|
|
@ -131,12 +132,12 @@ class ServiceList(AdvertisingDataField):
|
|||
def __get__(self, obj, cls):
|
||||
if not self._present(obj) and not obj.mutable:
|
||||
return None
|
||||
if not hasattr(obj, "_service_lists"):
|
||||
obj._service_lists = {}
|
||||
if not hasattr(obj, "adv_service_lists"):
|
||||
obj.adv_service_lists = {}
|
||||
first_adt = self.standard_services[0]
|
||||
if first_adt not in obj._service_lists:
|
||||
obj._service_lists[first_adt] = BoundServiceList(obj, **self.__dict__)
|
||||
return obj._service_lists[first_adt]
|
||||
if first_adt not in obj.adv_service_lists:
|
||||
obj.adv_service_lists[first_adt] = BoundServiceList(obj, **self.__dict__)
|
||||
return obj.adv_service_lists[first_adt]
|
||||
|
||||
class ProvideServicesAdvertisement(Advertisement):
|
||||
"""Advertise what services that the device makes available upon connection."""
|
||||
|
|
|
|||
72
adafruit_ble/attributes/__init__.py
Normal file
72
adafruit_ble/attributes/__init__.py
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2019 Dan Halbert for Adafruit Industries
|
||||
#
|
||||
# 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.
|
||||
"""
|
||||
:py:mod:`~adafruit_ble.attributes`
|
||||
====================================================
|
||||
|
||||
This module provides definitions common to all kinds of BLE attributes,
|
||||
specifically characteristics and descriptors.
|
||||
|
||||
"""
|
||||
import _bleio
|
||||
|
||||
__version__ = "0.0.0-auto.0"
|
||||
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
|
||||
|
||||
class Attribute:
|
||||
"""Constants describing security levels.
|
||||
|
||||
.. data:: NO_ACCESS
|
||||
|
||||
security mode: access not allowed
|
||||
|
||||
.. data:: OPEN
|
||||
|
||||
security_mode: no security (link is not encrypted)
|
||||
|
||||
.. data:: ENCRYPT_NO_MITM
|
||||
|
||||
security_mode: unauthenticated encryption, without man-in-the-middle protection
|
||||
|
||||
.. data:: ENCRYPT_WITH_MITM
|
||||
|
||||
security_mode: authenticated encryption, with man-in-the-middle protection
|
||||
|
||||
.. data:: LESC_ENCRYPT_WITH_MITM
|
||||
|
||||
security_mode: LESC encryption, with man-in-the-middle protection
|
||||
|
||||
.. data:: SIGNED_NO_MITM
|
||||
|
||||
security_mode: unauthenticated data signing, without man-in-the-middle protection
|
||||
|
||||
.. data:: SIGNED_WITH_MITM
|
||||
|
||||
security_mode: authenticated data signing, without man-in-the-middle protection
|
||||
"""
|
||||
NO_ACCESS = _bleio.Attribute.NO_ACCESS
|
||||
OPEN = _bleio.Attribute.OPEN
|
||||
ENCRYPT_NO_MITM = _bleio.Attribute.ENCRYPT_NO_MITM
|
||||
ENCRYPT_WITH_MITM = _bleio.Attribute.ENCRYPT_WITH_MITM
|
||||
LESC_ENCRYPT_WITH_MITM = _bleio.Attribute.LESC_ENCRYPT_WITH_MITM
|
||||
SIGNED_NO_MITM = _bleio.Attribute.SIGNED_NO_MITM
|
||||
SIGNED_WITH_MITM = _bleio.Attribute.SIGNED_NO_MITM
|
||||
|
|
@ -30,32 +30,90 @@ This module provides core BLE characteristic classes that are used within Servic
|
|||
import struct
|
||||
import _bleio
|
||||
|
||||
from ..attributes import Attribute
|
||||
|
||||
__version__ = "0.0.0-auto.0"
|
||||
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
|
||||
|
||||
|
||||
class Characteristic:
|
||||
"""Top level Characteristic class that does basic binding."""
|
||||
def __init__(self, *, uuid=None, initial_value=None, max_length=None, **kwargs):
|
||||
"""
|
||||
Top level Characteristic class that does basic binding.
|
||||
|
||||
:param UUID uuid: The uuid of the characteristic
|
||||
:param int properties: The properties of the characteristic,
|
||||
specified as a bitmask of these values bitwise-or'd together:
|
||||
`BROADCAST`, `INDICATE`, `NOTIFY`, `READ`, `WRITE`, `WRITE_NO_RESPONSE`.
|
||||
:param int read_perm: Specifies whether the characteristic can be read by a client,
|
||||
and if so, which security mode is required.
|
||||
Must be one of the integer values `Attribute.NO_ACCESS`, `Attribute.OPEN`,
|
||||
`Attribute.ENCRYPT_NO_MITM`, `Attribute.ENCRYPT_WITH_MITM`,
|
||||
`Attribute.LESC_ENCRYPT_WITH_MITM`,
|
||||
`Attribute.SIGNED_NO_MITM`, or `Attribute.SIGNED_WITH_MITM`.
|
||||
:param int write_perm: Specifies whether the characteristic can be written by a client,
|
||||
and if so, which security mode is required. Values allowed are the same as ``read_perm``.
|
||||
:param int max_length: Maximum length in bytes of the characteristic value. The maximum allowed
|
||||
is 512, or possibly 510 if ``fixed_length`` is False. The default, 20, is the maximum
|
||||
number of data bytes that fit in a single BLE 4.x ATT packet.
|
||||
:param bool fixed_length: True if the characteristic value is of fixed length.
|
||||
:param buf initial_value: The initial value for this characteristic. If not given, will be
|
||||
filled with zeros.
|
||||
|
||||
.. data:: BROADCAST
|
||||
|
||||
property: allowed in advertising packets
|
||||
|
||||
.. data:: INDICATE
|
||||
|
||||
property: server will indicate to the client when the value is set and wait for a response
|
||||
|
||||
.. data:: NOTIFY
|
||||
|
||||
property: server will notify the client when the value is set
|
||||
|
||||
.. data:: READ
|
||||
|
||||
property: clients may read this characteristic
|
||||
|
||||
.. data:: WRITE
|
||||
|
||||
property: clients may write this characteristic; a response will be sent back
|
||||
|
||||
.. data:: WRITE_NO_RESPONSE
|
||||
|
||||
property: clients may write this characteristic; no response will be sent back
|
||||
"""
|
||||
BROADCAST = _bleio.Characteristic.BROADCAST
|
||||
INDICATE = _bleio.Characteristic.INDICATE
|
||||
NOTIFY = _bleio.Characteristic.NOTIFY
|
||||
READ = _bleio.Characteristic.READ
|
||||
WRITE = _bleio.Characteristic.WRITE
|
||||
WRITE_NO_RESPONSE = _bleio.Characteristic.WRITE_NO_RESPONSE
|
||||
|
||||
def __init__(self, *, uuid=None, properties=0,
|
||||
read_perm=Attribute.OPEN, write_perm=Attribute.OPEN,
|
||||
max_length=20, fixed_length=False, initial_value=None):
|
||||
self.field_name = None # Set by Service during basic binding
|
||||
|
||||
if uuid:
|
||||
self.uuid = uuid
|
||||
self.kwargs = kwargs
|
||||
self.initial_value = initial_value
|
||||
self.properties = properties
|
||||
self.read_perm = read_perm
|
||||
self.write_perm = write_perm
|
||||
self.max_length = max_length
|
||||
self.field_name = None # Set by Service during basic binding
|
||||
self.fixed_length = fixed_length
|
||||
self.initial_value = initial_value
|
||||
|
||||
def _ensure_bound(self, service, initial_value=None):
|
||||
"""Binds the characteristic to the local Service or remote Characteristic object given."""
|
||||
if self.field_name in service.bleio_characteristics:
|
||||
return
|
||||
if service.remote:
|
||||
bleio_characteristic = None
|
||||
remote_characteristics = service.bleio_service.characteristics
|
||||
for characteristic in remote_characteristics:
|
||||
for characteristic in service.bleio_service.characteristics:
|
||||
if characteristic.uuid == self.uuid.bleio_uuid:
|
||||
bleio_characteristic = characteristic
|
||||
break
|
||||
if not bleio_characteristic:
|
||||
else:
|
||||
raise AttributeError("Characteristic not available on remote service")
|
||||
else:
|
||||
bleio_characteristic = self.__bind_locally(service, initial_value)
|
||||
|
|
@ -74,18 +132,14 @@ class Characteristic:
|
|||
if max_length is None:
|
||||
max_length = len(initial_value)
|
||||
return _bleio.Characteristic.add_to_service(
|
||||
service.bleio_service,
|
||||
self.uuid.bleio_uuid,
|
||||
initial_value=initial_value,
|
||||
max_length=max_length,
|
||||
**self.kwargs
|
||||
)
|
||||
service.bleio_service, self.uuid.bleio_uuid, initial_value=initial_value,
|
||||
max_length=max_length, fixed_length=self.fixed_length,
|
||||
properties=self.properties, read_perm=self.read_perm, write_perm=self.write_perm)
|
||||
|
||||
def __get__(self, service, cls=None):
|
||||
self._ensure_bound(service)
|
||||
bleio_characteristic = service.bleio_characteristics[self.field_name]
|
||||
raw_data = bleio_characteristic.value
|
||||
return raw_data
|
||||
return bleio_characteristic.value
|
||||
|
||||
def __set__(self, service, value):
|
||||
self._ensure_bound(service, value)
|
||||
|
|
@ -93,28 +147,36 @@ class Characteristic:
|
|||
bleio_characteristic.value = value
|
||||
|
||||
class ComplexCharacteristic:
|
||||
"""Characteristic class that does complex binding where the subclass returns a full object for
|
||||
interacting with the characteristic data. The Characteristic itself will be shadowed once it
|
||||
has been bound to the corresponding instance attribute."""
|
||||
def __init__(self, *, uuid=None, **kwargs):
|
||||
"""
|
||||
Characteristic class that does complex binding where the subclass returns a full object for
|
||||
interacting with the characteristic data. The Characteristic itself will be shadowed once it
|
||||
has been bound to the corresponding instance attribute.
|
||||
"""
|
||||
def __init__(self, *, uuid=None, properties=0,
|
||||
read_perm=Attribute.OPEN, write_perm=Attribute.OPEN,
|
||||
max_length=20, fixed_length=False, initial_value=None):
|
||||
self.field_name = None # Set by Service during basic binding
|
||||
|
||||
if uuid:
|
||||
self.uuid = uuid
|
||||
self.kwargs = kwargs
|
||||
self.field_name = None # Set by Service
|
||||
self.properties = properties
|
||||
self.read_perm = read_perm
|
||||
self.write_perm = write_perm
|
||||
self.max_length = max_length
|
||||
self.fixed_length = fixed_length
|
||||
self.initial_value = initial_value
|
||||
|
||||
def bind(self, service):
|
||||
"""Binds the characteristic to the local Service or remote Characteristic object given."""
|
||||
if service.remote:
|
||||
remote_characteristics = service.bleio_service.characteristics
|
||||
for characteristic in remote_characteristics:
|
||||
for characteristic in service.bleio_service.characteristics:
|
||||
if characteristic.uuid == self.uuid.bleio_uuid:
|
||||
return characteristic
|
||||
raise AttributeError("Characteristic not available on remote service")
|
||||
return _bleio.Characteristic.add_to_service(
|
||||
service.bleio_service,
|
||||
self.uuid.bleio_uuid,
|
||||
**self.kwargs
|
||||
)
|
||||
service.bleio_service, self.uuid.bleio_uuid,
|
||||
initial_value=self.initial_value, max_length=self.max_length,
|
||||
properties=self.properties, read_perm=self.read_perm, write_perm=self.write_perm)
|
||||
|
||||
def __get__(self, service, cls=None):
|
||||
bound_object = self.bind(service)
|
||||
|
|
@ -122,13 +184,27 @@ class ComplexCharacteristic:
|
|||
return bound_object
|
||||
|
||||
class StructCharacteristic(Characteristic):
|
||||
"""Data descriptor for a structure with a fixed format."""
|
||||
def __init__(self, struct_format, **kwargs):
|
||||
"""
|
||||
Data descriptor for a structure with a fixed format.
|
||||
|
||||
:param struct_format: a `struct` format string describing how to pack multiple values
|
||||
into the characteristic bytestring
|
||||
:param UUID uuid: The uuid of the characteristic
|
||||
:param int properties: see `Characteristic`
|
||||
:param int read_perm: see `Characteristic`
|
||||
:param int write_perm: see `Characteristic`
|
||||
:param buf initial_value: see `Characteristic`
|
||||
"""
|
||||
def __init__(self, struct_format, *, uuid=None, properties=0,
|
||||
read_perm=Attribute.OPEN, write_perm=Attribute.OPEN,
|
||||
initial_value=None):
|
||||
self._struct_format = struct_format
|
||||
self._expected_size = struct.calcsize(struct_format)
|
||||
if "initial_value" in kwargs:
|
||||
kwargs["initial_value"] = struct.pack(self._struct_format, *kwargs["initial_value"])
|
||||
super().__init__(**kwargs, max_length=self._expected_size, fixed_length=True)
|
||||
if initial_value:
|
||||
initial_value = struct.pack(self._struct_format, initial_value)
|
||||
super().__init__(uuid=uuid, initial_value=initial_value,
|
||||
max_length=self._expected_size, fixed_length=True,
|
||||
properties=properties, read_perm=read_perm, write_perm=write_perm)
|
||||
|
||||
def __get__(self, obj, cls=None):
|
||||
raw_data = super().__get__(obj, cls)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ This module provides float characteristics that are usable directly as attribute
|
|||
|
||||
"""
|
||||
|
||||
from . import Attribute
|
||||
from . import StructCharacteristic
|
||||
|
||||
__version__ = "0.0.0-auto.0"
|
||||
|
|
@ -34,11 +35,14 @@ __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
|
|||
|
||||
class FloatCharacteristic(StructCharacteristic):
|
||||
"""32-bit float"""
|
||||
# TODO: Valid set values as within range.
|
||||
def __init__(self, **kwargs):
|
||||
if "initial_value" in kwargs:
|
||||
kwargs["initial_value"] = (kwargs["initial_value"],)
|
||||
super().__init__("<f", **kwargs)
|
||||
def __init__(self, *, uuid=None, properties=0,
|
||||
read_perm=Attribute.OPEN, write_perm=Attribute.OPEN,
|
||||
initial_value=None):
|
||||
if initial_value:
|
||||
initial_value = (initial_value,)
|
||||
super().__init__("<f", uuid=uuid, properties=properties,
|
||||
read_perm=read_perm, write_perm=write_perm,
|
||||
initial_value=initial_value)
|
||||
|
||||
def __get__(self, obj, cls=None):
|
||||
return super().__get__(obj)[0]
|
||||
|
|
|
|||
|
|
@ -27,20 +27,28 @@ This module provides integer characteristics that are usable directly as attribu
|
|||
|
||||
"""
|
||||
|
||||
from . import Attribute
|
||||
from . import StructCharacteristic
|
||||
|
||||
__version__ = "0.0.0-auto.0"
|
||||
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
|
||||
|
||||
class Uint8Characteristic(StructCharacteristic):
|
||||
"""Uint8 number."""
|
||||
# TODO: Valid set values as within range.
|
||||
def __init__(self, *, min_value=0, max_value=255, **kwargs):
|
||||
class IntCharacteristic(StructCharacteristic):
|
||||
"""Superclass for different kinds of integer fields."""
|
||||
def __init__(self, format_string, min_value, max_value, *, uuid=None, properties=0,
|
||||
read_perm=Attribute.OPEN, write_perm=Attribute.OPEN,
|
||||
initial_value=None):
|
||||
self._min_value = min_value
|
||||
self._max_value = max_value
|
||||
if "initial_value" in kwargs:
|
||||
kwargs["initial_value"] = (kwargs["initial_value"],)
|
||||
super().__init__("<B", **kwargs)
|
||||
if initial_value:
|
||||
initial_value = (initial_value,)
|
||||
if not self._min_value <= initial_value <= self._max_value:
|
||||
raise ValueError("initial_value out of range")
|
||||
|
||||
|
||||
super().__init__(format_string, uuid=uuid, properties=properties,
|
||||
read_perm=read_perm, write_perm=write_perm,
|
||||
initial_value=initial_value)
|
||||
|
||||
def __get__(self, obj, cls=None):
|
||||
return super().__get__(obj)[0]
|
||||
|
|
@ -49,3 +57,33 @@ class Uint8Characteristic(StructCharacteristic):
|
|||
if not self._min_value <= value <= self._max_value:
|
||||
raise ValueError("out of range")
|
||||
super().__set__(obj, (value,))
|
||||
|
||||
class Int8Characteristic(IntCharacteristic):
|
||||
"""Int8 number."""
|
||||
def __init__(self, *, min_value=-128, max_value=127, **kwargs):
|
||||
super().__init__("<b", min_value, max_value, **kwargs)
|
||||
|
||||
class Uint8Characteristic(IntCharacteristic):
|
||||
"""Uint8 number."""
|
||||
def __init__(self, *, min_value=0, max_value=0xff, **kwargs):
|
||||
super().__init__("<B", min_value, max_value, **kwargs)
|
||||
|
||||
class Int16Characteristic(IntCharacteristic):
|
||||
"""Int16 number."""
|
||||
def __init__(self, *, min_value=-32768, max_value=32767, **kwargs):
|
||||
super().__init__("<h", min_value, max_value, **kwargs)
|
||||
|
||||
class Uint16Characteristic(IntCharacteristic):
|
||||
"""Uint16 number."""
|
||||
def __init__(self, *, min_value=0, max_value=0xffff, **kwargs):
|
||||
super().__init__("<H", min_value, max_value, **kwargs)
|
||||
|
||||
class Int32Characteristic(IntCharacteristic):
|
||||
"""Int32 number."""
|
||||
def __init__(self, *, min_value=-2147483648, max_value=2147483647, **kwargs):
|
||||
super().__init__("<i", min_value, max_value, **kwargs)
|
||||
|
||||
class Uint32Characteristic(IntCharacteristic):
|
||||
"""Uint32 number."""
|
||||
def __init__(self, *, min_value=0, max_value=0xffffffff, **kwargs):
|
||||
super().__init__("<I", min_value, max_value, **kwargs)
|
||||
|
|
|
|||
|
|
@ -27,8 +27,10 @@ This module provides stream characteristics that bind readable or writable objec
|
|||
object they are on.
|
||||
|
||||
"""
|
||||
|
||||
import _bleio
|
||||
|
||||
from . import Attribute
|
||||
from . import Characteristic
|
||||
from . import ComplexCharacteristic
|
||||
|
||||
__version__ = "0.0.0-auto.0"
|
||||
|
|
@ -49,12 +51,13 @@ class BoundWriteStream:
|
|||
|
||||
class StreamOut(ComplexCharacteristic):
|
||||
"""Output stream from the Service server."""
|
||||
def __init__(self, *, timeout=1.0, buffer_size=64, **kwargs):
|
||||
def __init__(self, *, uuid=None, timeout=1.0, buffer_size=64,
|
||||
properties=Characteristic.NOTIFY,
|
||||
read_perm=Attribute.OPEN, write_perm=Attribute.OPEN):
|
||||
self._timeout = timeout
|
||||
self._buffer_size = buffer_size
|
||||
super().__init__(properties=_bleio.Characteristic.NOTIFY,
|
||||
read_perm=_bleio.Attribute.OPEN,
|
||||
**kwargs)
|
||||
super().__init__(uuid=uuid, properties=properties,
|
||||
read_perm=read_perm, write_perm=write_perm)
|
||||
|
||||
def bind(self, service):
|
||||
"""Binds the characteristic to the given Service."""
|
||||
|
|
@ -69,14 +72,14 @@ class StreamOut(ComplexCharacteristic):
|
|||
|
||||
class StreamIn(ComplexCharacteristic):
|
||||
"""Input stream into the Service server."""
|
||||
def __init__(self, *, timeout=1.0, buffer_size=64, **kwargs):
|
||||
def __init__(self, *, uuid=None, timeout=1.0, buffer_size=64,
|
||||
properties=(Characteristic.WRITE | Characteristic.WRITE_NO_RESPONSE),
|
||||
write_perm=Attribute.OPEN):
|
||||
self._timeout = timeout
|
||||
self._buffer_size = buffer_size
|
||||
super().__init__(properties=(_bleio.Characteristic.WRITE |
|
||||
_bleio.Characteristic.WRITE_NO_RESPONSE),
|
||||
read_perm=_bleio.Attribute.NO_ACCESS,
|
||||
write_perm=_bleio.Attribute.OPEN,
|
||||
**kwargs)
|
||||
super().__init__(uuid=uuid, properties=properties,
|
||||
read_perm=Attribute.NO_ACCESS,
|
||||
write_perm=write_perm)
|
||||
|
||||
def bind(self, service):
|
||||
"""Binds the characteristic to the given Service."""
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ This module provides string characteristics.
|
|||
|
||||
"""
|
||||
|
||||
import _bleio
|
||||
from . import Attribute
|
||||
from . import Characteristic
|
||||
|
||||
__version__ = "0.0.0-auto.0"
|
||||
|
|
@ -35,30 +35,27 @@ __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
|
|||
|
||||
class StringCharacteristic(Characteristic):
|
||||
"""UTF-8 Encoded string characteristic."""
|
||||
def __init__(self, *, properties=_bleio.Characteristic.READ, uuid=None):
|
||||
super().__init__(properties=properties,
|
||||
read_perm=_bleio.Attribute.OPEN,
|
||||
max_length=512,
|
||||
def __init__(self, *, uuid=None, properties=Characteristic.READ,
|
||||
read_perm=Attribute.OPEN, write_perm=Attribute.OPEN,
|
||||
initial_value=None):
|
||||
super().__init__(uuid=uuid, properties=properties,
|
||||
read_perm=read_perm, write_perm=write_perm,
|
||||
max_length=510, # shorter than 512 due to fixed_length==False
|
||||
fixed_length=False,
|
||||
uuid=uuid)
|
||||
initial_value=initial_value)
|
||||
|
||||
def __get__(self, obj, cls=None):
|
||||
raw_data = super().__get__(obj, cls)
|
||||
return str(raw_data, "utf-8")
|
||||
return str(super().__get__(obj, cls), "utf-8")
|
||||
|
||||
def __set__(self, obj, value):
|
||||
encoded_value = value.encode("utf-8")
|
||||
super().__set__(obj, encoded_value)
|
||||
super().__set__(obj, value.encode("utf-8"))
|
||||
|
||||
class FixedStringCharacteristic(Characteristic):
|
||||
"""Fixed strings are set once when bound and unchanged after."""
|
||||
def __init__(self, *, uuid=None):
|
||||
super().__init__(properties=_bleio.Characteristic.READ,
|
||||
read_perm=_bleio.Attribute.OPEN,
|
||||
write_perm=_bleio.Attribute.NO_ACCESS,
|
||||
fixed_length=True,
|
||||
uuid=uuid)
|
||||
def __init__(self, *, uuid=None, read_perm=Attribute.OPEN):
|
||||
super().__init__(uuid=uuid, properties=Characteristic.READ,
|
||||
read_perm=read_perm, write_perm=Attribute.NO_ACCESS,
|
||||
fixed_length=True)
|
||||
|
||||
def __get__(self, obj, cls=None):
|
||||
raw_data = super().__get__(obj, cls)
|
||||
return str(raw_data, "utf-8")
|
||||
return str(super().__get__(obj, cls), "utf-8")
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ class Service:
|
|||
if (not isinstance(value, Characteristic) and
|
||||
not isinstance(value, ComplexCharacteristic)):
|
||||
continue
|
||||
|
||||
value.field_name = class_attr
|
||||
|
||||
# Get or set every attribute to ensure that they are all bound up front. We could lazily
|
||||
|
|
|
|||
|
|
@ -27,11 +27,10 @@ This module provides Services defined by CircuitPython. **Out of date.**
|
|||
|
||||
"""
|
||||
|
||||
import _bleio
|
||||
|
||||
from . import Service
|
||||
from ..characteristics.string import StringCharacteristic
|
||||
from ..characteristics import Characteristic
|
||||
from ..characteristics.stream import StreamOut
|
||||
from ..characteristics.string import StringCharacteristic
|
||||
from ..uuid import VendorUUID
|
||||
|
||||
__version__ = "0.0.0-auto.0"
|
||||
|
|
@ -50,6 +49,5 @@ class CircuitPythonService(Service):
|
|||
Unimplemented."""
|
||||
uuid = CircuitPythonUUID(0x0100)
|
||||
filename = StringCharacteristic(uuid=CircuitPythonUUID(0x0200),
|
||||
properties=(_bleio.Characteristic.READ |
|
||||
_bleio.Characteristic.WRITE))
|
||||
properties=(Characteristic.READ | Characteristic.WRITE))
|
||||
contents = StreamOut(uuid=CircuitPythonUUID(0x0201))
|
||||
|
|
|
|||
|
|
@ -27,8 +27,6 @@ This module provides Services defined by the MIDI group.
|
|||
|
||||
"""
|
||||
|
||||
import _bleio
|
||||
|
||||
from . import Service
|
||||
from ..uuid import VendorUUID
|
||||
from ..characteristics import Characteristic
|
||||
|
|
@ -40,10 +38,10 @@ class MidiIOCharacteristic(Characteristic):
|
|||
"""Workhorse MIDI Characteristic that carries midi messages both directions. Unimplemented."""
|
||||
uuid = VendorUUID("7772E5DB-3868-4112-A1A9-F2669D106BF3")
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(properties=(_bleio.Characteristic.NOTIFY |
|
||||
_bleio.Characteristic.READ |
|
||||
_bleio.Characteristic.WRITE |
|
||||
_bleio.Characteristic.WRITE_NO_RESPONSE), **kwargs)
|
||||
super().__init__(properties=(Characteristic.NOTIFY |
|
||||
Characteristic.READ |
|
||||
Characteristic.WRITE |
|
||||
Characteristic.WRITE_NO_RESPONSE), **kwargs)
|
||||
|
||||
class MidiService(Service):
|
||||
"""BLE Service that transports MIDI messages. Unimplemented."""
|
||||
|
|
|
|||
|
|
@ -44,19 +44,7 @@ class UARTService(Service):
|
|||
:param int buffer_size: buffer up to this many bytes.
|
||||
If more bytes are received, older bytes will be discarded.
|
||||
|
||||
Example::
|
||||
|
||||
from adafruit_ble.uart_client import UARTClient
|
||||
|
||||
uart_client = UARTClient()
|
||||
uart_addresses = uart_client.scan()
|
||||
if uart_addresses:
|
||||
uart_client.connect(uarts[0].address, 5,
|
||||
service_uuids_whitelist=(UART.NUS_SERVICE_UUID,))
|
||||
else:
|
||||
raise Error("No UART servers found.")
|
||||
|
||||
uart_client.write('abc')
|
||||
See ``examples/ble_uart_echo_test.py`` for a usage example.
|
||||
"""
|
||||
# pylint: disable=no-member
|
||||
uuid = VendorUUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
|
||||
|
|
@ -102,7 +90,7 @@ class UARTService(Service):
|
|||
Read a line, ending in a newline character.
|
||||
|
||||
:return: the line read
|
||||
:rtype: int or None
|
||||
:rtype: bytes or None
|
||||
"""
|
||||
return self._rx.readline()
|
||||
|
||||
|
|
|
|||
|
|
@ -48,8 +48,8 @@ import os
|
|||
import sys
|
||||
import microcontroller
|
||||
|
||||
from ..core import Service
|
||||
from ...core.uuid import StandardUUID
|
||||
from .. import Service
|
||||
from ...uuid import StandardUUID
|
||||
from ...characteristics.string import FixedStringCharacteristic
|
||||
|
||||
__version__ = "0.0.0-auto.0"
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import struct
|
|||
from micropython import const
|
||||
|
||||
import _bleio
|
||||
from adafruit_ble.characteristics import Attribute
|
||||
from adafruit_ble.characteristics import Characteristic
|
||||
from adafruit_ble.characteristics.int import Uint8Characteristic
|
||||
from adafruit_ble.uuid import StandardUUID
|
||||
|
|
@ -77,8 +78,8 @@ class ReportIn:
|
|||
self._characteristic = _bleio.Characteristic.add_to_service(
|
||||
service.bleio_service,
|
||||
self.uuid.bleio_uuid,
|
||||
properties=_bleio.Characteristic.READ | _bleio.Characteristic.NOTIFY,
|
||||
read_perm=_bleio.Attribute.ENCRYPT_NO_MITM, write_perm=_bleio.Attribute.NO_ACCESS,
|
||||
properties=Characteristic.READ | Characteristic.NOTIFY,
|
||||
read_perm=Attribute.ENCRYPT_NO_MITM, write_perm=Attribute.NO_ACCESS,
|
||||
max_length=max_length, fixed_length=True)
|
||||
self._report_id = report_id
|
||||
self.usage_page = usage_page
|
||||
|
|
@ -86,7 +87,7 @@ class ReportIn:
|
|||
|
||||
_bleio.Descriptor.add_to_characteristic(
|
||||
self._characteristic, _REPORT_REF_DESCR_UUID,
|
||||
read_perm=_bleio.Attribute.ENCRYPT_NO_MITM, write_perm=_bleio.Attribute.NO_ACCESS,
|
||||
read_perm=Attribute.ENCRYPT_NO_MITM, write_perm=Attribute.NO_ACCESS,
|
||||
initial_value=struct.pack('<BB', self._report_id, _REPORT_TYPE_INPUT))
|
||||
|
||||
def send_report(self, report):
|
||||
|
|
@ -97,14 +98,14 @@ class ReportOut:
|
|||
"""A single HID report that receives HID data from a client."""
|
||||
uuid = StandardUUID(0x24ad)
|
||||
def __init__(self, service, report_id, usage_page, usage, *, max_length):
|
||||
self._characteristic = _bleio.Characteristic.add_to_service(
|
||||
self._characteristic = Characteristic.add_to_service(
|
||||
service.bleio_service,
|
||||
self.uuid.bleio_uuid,
|
||||
max_length=max_length,
|
||||
fixed_length=True,
|
||||
properties=(_bleio.Characteristic.READ | _bleio.Characteristic.WRITE |
|
||||
_bleio.Characteristic.WRITE_NO_RESPONSE),
|
||||
read_perm=_bleio.Attribute.ENCRYPT_NO_MITM, write_perm=_bleio.Attribute.ENCRYPT_NO_MITM
|
||||
properties=(Characteristic.READ | Characteristic.WRITE |
|
||||
Characteristic.WRITE_NO_RESPONSE),
|
||||
read_perm=Attribute.ENCRYPT_NO_MITM, write_perm=Attribute.ENCRYPT_NO_MITM
|
||||
)
|
||||
self._report_id = report_id
|
||||
self.usage_page = usage_page
|
||||
|
|
@ -112,7 +113,7 @@ class ReportOut:
|
|||
|
||||
_bleio.Descriptor.add_to_characteristic(
|
||||
self._characteristic, _REPORT_REF_DESCR_UUID,
|
||||
read_perm=_bleio.Attribute.ENCRYPT_NO_MITM, write_perm=_bleio.Attribute.NO_ACCESS,
|
||||
read_perm=Attribute.ENCRYPT_NO_MITM, write_perm=Attribute.NO_ACCESS,
|
||||
initial_value=struct.pack('<BB', self._report_id, _REPORT_TYPE_OUTPUT))
|
||||
|
||||
_ITEM_TYPE_MAIN = const(0)
|
||||
|
|
@ -142,25 +143,25 @@ class HIDService(Service):
|
|||
default_field_name = "hid"
|
||||
|
||||
boot_keyboard_in = Characteristic(uuid=StandardUUID(0x2A22),
|
||||
properties=(_bleio.Characteristic.READ |
|
||||
_bleio.Characteristic.NOTIFY),
|
||||
read_perm=_bleio.Attribute.ENCRYPT_NO_MITM,
|
||||
write_perm=_bleio.Attribute.NO_ACCESS,
|
||||
properties=(Characteristic.READ |
|
||||
Characteristic.NOTIFY),
|
||||
read_perm=Attribute.ENCRYPT_NO_MITM,
|
||||
write_perm=Attribute.NO_ACCESS,
|
||||
max_length=8, fixed_length=True)
|
||||
|
||||
boot_keyboard_out = Characteristic(uuid=StandardUUID(0x2A32),
|
||||
properties=(_bleio.Characteristic.READ |
|
||||
_bleio.Characteristic.WRITE |
|
||||
_bleio.Characteristic.WRITE_NO_RESPONSE),
|
||||
read_perm=_bleio.Attribute.ENCRYPT_NO_MITM,
|
||||
write_perm=_bleio.Attribute.ENCRYPT_NO_MITM,
|
||||
properties=(Characteristic.READ |
|
||||
Characteristic.WRITE |
|
||||
Characteristic.WRITE_NO_RESPONSE),
|
||||
read_perm=Attribute.ENCRYPT_NO_MITM,
|
||||
write_perm=Attribute.ENCRYPT_NO_MITM,
|
||||
max_length=1, fixed_length=True)
|
||||
|
||||
protocol_mode = Uint8Characteristic(uuid=StandardUUID(0x2A4E),
|
||||
properties=(_bleio.Characteristic.READ |
|
||||
_bleio.Characteristic.WRITE_NO_RESPONSE),
|
||||
read_perm=_bleio.Attribute.OPEN,
|
||||
write_perm=_bleio.Attribute.OPEN,
|
||||
properties=(Characteristic.READ |
|
||||
Characteristic.WRITE_NO_RESPONSE),
|
||||
read_perm=Attribute.OPEN,
|
||||
write_perm=Attribute.OPEN,
|
||||
initial_value=1, max_value=1)
|
||||
"""Protocol mode: boot (0) or report (1)"""
|
||||
|
||||
|
|
@ -169,24 +170,24 @@ class HIDService(Service):
|
|||
# bcd1.1, country = 0, flag = normal connect
|
||||
# TODO: Make this a struct.
|
||||
hid_information = Characteristic(uuid=StandardUUID(0x2A4A),
|
||||
properties=_bleio.Characteristic.READ,
|
||||
read_perm=_bleio.Attribute.ENCRYPT_NO_MITM,
|
||||
write_perm=_bleio.Attribute.NO_ACCESS,
|
||||
properties=Characteristic.READ,
|
||||
read_perm=Attribute.ENCRYPT_NO_MITM,
|
||||
write_perm=Attribute.NO_ACCESS,
|
||||
initial_value=b'\x01\x01\x00\x02')
|
||||
"""Hid information including version, country code and flags."""
|
||||
|
||||
report_map = Characteristic(uuid=StandardUUID(0x2A4B),
|
||||
properties=_bleio.Characteristic.READ,
|
||||
read_perm=_bleio.Attribute.ENCRYPT_NO_MITM,
|
||||
write_perm=_bleio.Attribute.NO_ACCESS,
|
||||
properties=Characteristic.READ,
|
||||
read_perm=Attribute.ENCRYPT_NO_MITM,
|
||||
write_perm=Attribute.NO_ACCESS,
|
||||
fixed_length=True)
|
||||
"""This is the USB HID descriptor (not to be confused with a BLE Descriptor). It describes
|
||||
which report characteristic are what."""
|
||||
|
||||
suspended = Uint8Characteristic(uuid=StandardUUID(0x2A4C),
|
||||
properties=_bleio.Characteristic.WRITE_NO_RESPONSE,
|
||||
read_perm=_bleio.Attribute.NO_ACCESS,
|
||||
write_perm=_bleio.Attribute.ENCRYPT_NO_MITM,
|
||||
properties=Characteristic.WRITE_NO_RESPONSE,
|
||||
read_perm=Attribute.NO_ACCESS,
|
||||
write_perm=Attribute.ENCRYPT_NO_MITM,
|
||||
max_value=1)
|
||||
"""Controls whether the device should be suspended (0) or not (1)."""
|
||||
|
||||
|
|
|
|||
|
|
@ -29,11 +29,11 @@ This module provides Service classes for BLE defined standard services.
|
|||
|
||||
import time
|
||||
|
||||
from ..core import Service
|
||||
from ...core.uuid import StandardUUID
|
||||
from ..characteristics.string import StringCharacteristic
|
||||
from ..characteristics.core import StructCharacteristic
|
||||
from ..characteristics.int import Uint8Characteristic
|
||||
from .. import Service
|
||||
from ...uuid import StandardUUID
|
||||
from ...characteristics.string import StringCharacteristic
|
||||
from ...characteristics import StructCharacteristic
|
||||
from ...characteristics.int import Uint8Characteristic
|
||||
|
||||
__version__ = "0.0.0-auto.0"
|
||||
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
|
||||
|
|
@ -83,6 +83,6 @@ class CurrentTimeService(Service):
|
|||
"""The current time as a `time.struct_time`. Day of year and whether DST is in effect
|
||||
are always -1.
|
||||
"""
|
||||
_, month, day, hour, minute, second, weekday, _, _ = self.current_time
|
||||
year, month, day, hour, minute, second, weekday, _, _ = self.current_time
|
||||
# Bluetooth weekdays count from 1. struct_time counts from 0.
|
||||
return time.struct_time((month, day, hour, minute, second, weekday - 1, -1))
|
||||
return time.struct_time((year, month, day, hour, minute, second, weekday - 1, -1, -1))
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ class UUID:
|
|||
self.bleio_uuid.pack_into(buffer, offset=offset)
|
||||
|
||||
class StandardUUID(UUID):
|
||||
"""Bluetooth defined, 16-bit UUID."""
|
||||
"""Standard 16-bit UUID defined by the Bluetooth SIG."""
|
||||
def __init__(self, uuid16):
|
||||
if not isinstance(uuid16, int):
|
||||
uuid16 = struct.unpack("<H", uuid16)[0]
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
top_level
|
||||
advertising
|
||||
attributes
|
||||
characteristics
|
||||
services
|
||||
uuid
|
||||
|
|
|
|||
5
docs/attributes.rst
Normal file
5
docs/attributes.rst
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
`adafruit_ble.attributes`
|
||||
====================================================
|
||||
|
||||
.. automodule:: adafruit_ble.attributes
|
||||
:members:
|
||||
|
|
@ -1,12 +1,20 @@
|
|||
class Attribute:
|
||||
NO_ACCESS = 0
|
||||
OPEN = 0
|
||||
ENCRYPT_NO_MITM = 0
|
||||
ENCRYPT_WITH_MITM = 0
|
||||
LESC_ENCRYPT_WITH_MITM = 0
|
||||
SIGNED_NO_MITM = 0
|
||||
SIGNED_WITH_MITM = 0
|
||||
|
||||
class UUID:
|
||||
def __init__(self, uuid):
|
||||
pass
|
||||
|
||||
class Characteristic:
|
||||
BROADCAST = 0
|
||||
READ = 0
|
||||
WRITE = 0
|
||||
NOTIFY = 0
|
||||
INDICATE = 0
|
||||
WRITE_NO_RESPONSE = 0
|
||||
|
|
|
|||
Loading…
Reference in a new issue