Merge pull request #74 from adafruit/pylint-update

Ran black, updated to pylint 2.x
This commit is contained in:
Kattni 2020-03-17 15:13:20 -04:00 committed by GitHub
commit 2052ff6da3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 779 additions and 427 deletions

View file

@ -40,7 +40,7 @@ jobs:
source actions-ci/install.sh
- name: Pip install pylint, black, & Sphinx
run: |
pip install --force-reinstall pylint==1.9.2 black==19.10b0 Sphinx sphinx-rtd-theme
pip install --force-reinstall pylint black==19.10b0 Sphinx sphinx-rtd-theme
- name: Library version
run: git describe --dirty --always --tags
- name: PyLint

View file

@ -26,12 +26,14 @@ This module provides higher-level BLE (Bluetooth Low Energy) functionality,
building on the native `_bleio` module.
"""
#pylint: disable=wrong-import-position
# pylint: disable=wrong-import-position
import sys
if sys.implementation.name == 'circuitpython' and sys.implementation.version[0] <= 4:
if sys.implementation.name == "circuitpython" and sys.implementation.version[0] <= 4:
raise ImportError(
"This release is not compatible with CircuitPython 4.x; use library release 1.x.x")
#pylint: enable=wrong-import-position
"This release is not compatible with CircuitPython 4.x; use library release 1.x.x"
)
# pylint: enable=wrong-import-position
import _bleio
@ -41,6 +43,7 @@ from .advertising import Advertisement
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
class BLEConnection:
"""
Represents a connection to a peer BLE device.
@ -49,6 +52,7 @@ class BLEConnection:
: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.
@ -61,7 +65,9 @@ class BLEConnection:
if uuid in self._discovered_bleio_services:
remote_service = self._discovered_bleio_services[uuid]
else:
results = self._bleio_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_bleio_services[uuid] = remote_service
@ -140,6 +146,7 @@ class BLEConnection:
"""Disconnect from peer."""
self._bleio_connection.disconnect()
class BLERadio:
"""
BLERadio provides the interfaces for BLE advertising,
@ -172,17 +179,28 @@ class BLERadio:
scan_response.tx_power = self.tx_power
if scan_response:
scan_response_bytes = bytes(scan_response)
self._adapter.start_advertising(advertisement_bytes,
scan_response=scan_response_bytes,
connectable=advertisement.connectable,
interval=interval)
self._adapter.start_advertising(
advertisement_bytes,
scan_response=scan_response_bytes,
connectable=advertisement.connectable,
interval=interval,
)
def stop_advertising(self):
"""Stops advertising."""
self._adapter.stop_advertising()
def start_scan(self, *advertisement_types, buffer_size=512, extended=False, timeout=None,
interval=0.1, window=0.1, minimum_rssi=-80, active=True):
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
@ -214,10 +232,16 @@ class BLERadio:
prefixes = b""
if advertisement_types:
prefixes = b"".join(adv.prefix for adv in advertisement_types)
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):
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):

View file

@ -25,13 +25,16 @@ Advertising is the first phase of BLE where devices can broadcast
import struct
def to_hex(seq):
"""Pretty prints a byte sequence as hex values."""
return " ".join("{:02x}".format(v) for v in seq)
def to_bytes_literal(seq):
"""Prints a byte sequence as a Python bytes literal that only uses hex encoding."""
return "b\"" + "".join("\\x{:02x}".format(v) for v in seq) + "\""
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
@ -45,7 +48,7 @@ def decode_data(data, *, key_encoding="B"):
if item_length == 0:
break
key = struct.unpack_from(key_encoding, data, i)[0]
value = data[i + key_size:i + item_length]
value = data[i + key_size : i + item_length]
if key in data_dict:
if not isinstance(data_dict[key], list):
data_dict[key] = [data_dict[key]]
@ -55,6 +58,7 @@ def decode_data(data, *, key_encoding="B"):
i += item_length
return data_dict
def compute_length(data_dict, *, key_encoding="B"):
"""Computes the length of the encoded data dictionary."""
value_size = 0
@ -66,6 +70,7 @@ def compute_length(data_dict, *, key_encoding="B"):
value_size += len(value)
return len(data_dict) + len(data_dict) * struct.calcsize(key_encoding) + value_size
def encode_data(data_dict, *, key_encoding="B"):
"""Helper which encodes dictionaries into length encoded structures with the given key
encoding."""
@ -79,17 +84,21 @@ def encode_data(data_dict, *, key_encoding="B"):
item_length = key_size + len(value)
struct.pack_into("B", data, i, item_length)
struct.pack_into(key_encoding, data, i + 1, key)
data[i + 1 + key_size: i + 1 + item_length] = bytes(value)
data[i + 1 + key_size : i + 1 + item_length] = bytes(value)
i += 1 + item_length
return data
class AdvertisingDataField:
"""Top level class for any descriptor classes that live in Advertisement or its subclasses."""
# pylint: disable=too-few-public-methods,unnecessary-pass
pass
class AdvertisingFlag:
"""A single bit flag within an AdvertisingFlags object."""
def __init__(self, bit_position):
self._bitmask = 1 << bit_position
@ -102,6 +111,7 @@ class AdvertisingFlag:
else:
obj.flags &= ~self._bitmask
class AdvertisingFlags(AdvertisingDataField):
"""Standard advertising flags"""
@ -135,10 +145,12 @@ class AdvertisingFlags(AdvertisingDataField):
parts.append(attr)
return "<AdvertisingFlags {} >".format(" ".join(parts))
class String(AdvertisingDataField):
"""UTF-8 encoded string in an Advertisement.
Not null terminated once encoded because length is always transmitted."""
def __init__(self, *, advertising_data_type):
self._adt = advertising_data_type
@ -152,8 +164,10 @@ class String(AdvertisingDataField):
def __set__(self, obj, value):
obj.data_dict[self._adt] = value.encode("utf-8")
class Struct(AdvertisingDataField):
"""`struct` encoded data in an Advertisement."""
def __init__(self, struct_format, *, advertising_data_type):
self._format = struct_format
self._adt = advertising_data_type
@ -171,6 +185,7 @@ class Struct(AdvertisingDataField):
class LazyObjectField(AdvertisingDataField):
"""Non-data descriptor useful for lazily binding a complex object to an advertisement object."""
def __init__(self, cls, attribute_name, *, advertising_data_type, **kwargs):
self._cls = cls
self._attribute_name = attribute_name
@ -197,15 +212,17 @@ class LazyObjectField(AdvertisingDataField):
# TODO: Add __set_name__ support to CircuitPython so that we automatically tell the descriptor
# instance the attribute name it has and the class it is on.
class Advertisement:
"""Core Advertisement type"""
prefix = b"\x00" # This is an empty prefix and will match everything.
prefix = b"\x00" # This is an empty prefix and will match everything.
flags = LazyObjectField(AdvertisingFlags, "flags", advertising_data_type=0x01)
short_name = String(advertising_data_type=0x08)
"""Short local device name (shortened to fit)."""
complete_name = String(advertising_data_type=0x09)
"""Complete local device name."""
tx_power = Struct("<b", advertising_data_type=0x0a)
tx_power = Struct("<b", advertising_data_type=0x0A)
"""Transmit power level"""
# DEVICE_ID = 0x10
# """Device identifier."""
@ -242,7 +259,7 @@ class Advertisement:
self = cls()
self.data_dict = decode_data(entry.advertisement_bytes)
self.address = entry.address
self._rssi = entry.rssi # pylint: disable=protected-access
self._rssi = entry.rssi # pylint: disable=protected-access
self.connectable = entry.connectable
self.scan_response = entry.scan_response
self.mutable = False
@ -272,8 +289,10 @@ class Advertisement:
for attr in dir(self.__class__):
attribute_instance = getattr(self.__class__, attr)
if issubclass(attribute_instance.__class__, AdvertisingDataField):
if (issubclass(attribute_instance.__class__, LazyObjectField) and
not attribute_instance.advertising_data_type in self.data_dict):
if (
issubclass(attribute_instance.__class__, LazyObjectField)
and not attribute_instance.advertising_data_type in self.data_dict
):
# Skip uninstantiated lazy objects; if we get
# their value, they will be be instantiated.
continue
@ -286,4 +305,6 @@ class Advertisement:
return compute_length(self.data_dict)
def __repr__(self):
return "Advertisement(data={})".format(to_bytes_literal(encode_data(self.data_dict)))
return "Advertisement(data={})".format(
to_bytes_literal(encode_data(self.data_dict))
)

View file

@ -40,24 +40,29 @@ from .standard import ManufacturerData, ManufacturerDataField
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
_MANUFACTURING_DATA_ADT = const(0xff)
_MANUFACTURING_DATA_ADT = const(0xFF)
_ADAFRUIT_COMPANY_ID = const(0x0822)
_COLOR_DATA_ID = const(0x0000)
class AdafruitColor(Advertisement):
"""Broadcast a single RGB color."""
# This prefix matches all
prefix = struct.pack("<BBHBH",
0x6,
_MANUFACTURING_DATA_ADT,
_ADAFRUIT_COMPANY_ID,
struct.calcsize("<HI"),
_COLOR_DATA_ID)
manufacturer_data = LazyObjectField(ManufacturerData,
"manufacturer_data",
advertising_data_type=_MANUFACTURING_DATA_ADT,
company_id=_ADAFRUIT_COMPANY_ID,
key_encoding="<H")
prefix = struct.pack(
"<BBHBH",
0x6,
_MANUFACTURING_DATA_ADT,
_ADAFRUIT_COMPANY_ID,
struct.calcsize("<HI"),
_COLOR_DATA_ID,
)
manufacturer_data = LazyObjectField(
ManufacturerData,
"manufacturer_data",
advertising_data_type=_MANUFACTURING_DATA_ADT,
company_id=_ADAFRUIT_COMPANY_ID,
key_encoding="<H",
)
color = ManufacturerDataField(_COLOR_DATA_ID, "<I")
"""Color to broadcast as RGB integer."""

View file

@ -30,14 +30,23 @@ even though multiple purposes may actually be present in a single packet.
import struct
from . import Advertisement, AdvertisingDataField, encode_data, decode_data, to_hex, compute_length
from . import (
Advertisement,
AdvertisingDataField,
encode_data,
decode_data,
to_hex,
compute_length,
)
from ..uuid import StandardUUID, VendorUUID
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
class BoundServiceList:
"""Sequence-like object of Service UUID objects. It stores both standard and vendor UUIDs."""
def __init__(self, advertisement, *, standard_services, vendor_services):
self._advertisement = advertisement
self._standard_service_fields = standard_services
@ -48,13 +57,13 @@ class BoundServiceList:
if adt in self._advertisement.data_dict:
data = self._advertisement.data_dict[adt]
for i in range(len(data) // 2):
uuid = StandardUUID(data[2*i:2*(i+1)])
uuid = StandardUUID(data[2 * i : 2 * (i + 1)])
self._standard_services.append(uuid)
for adt in vendor_services:
if adt in self._advertisement.data_dict:
data = self._advertisement.data_dict[adt]
for i in range(len(data) // 16):
uuid = VendorUUID(data[16*i:16*(i+1)])
uuid = VendorUUID(data[16 * i : 16 * (i + 1)])
self._vendor_services.append(uuid)
def __contains__(self, key):
@ -83,10 +92,16 @@ class BoundServiceList:
# TODO: Differentiate between complete and incomplete lists.
def append(self, service):
"""Append a service to the list."""
if isinstance(service.uuid, StandardUUID) and service not in self._standard_services:
if (
isinstance(service.uuid, StandardUUID)
and service not in self._standard_services
):
self._standard_services.append(service.uuid)
self._update(self._standard_service_fields[0], self._standard_services)
elif isinstance(service.uuid, VendorUUID) and service not in self._vendor_services:
elif (
isinstance(service.uuid, VendorUUID)
and service not in self._vendor_services
):
self._vendor_services.append(service.uuid)
self._update(self._vendor_service_fields[0], self._vendor_services)
@ -96,11 +111,16 @@ class BoundServiceList:
standard = False
vendor = False
for service in services:
if (isinstance(service.uuid, StandardUUID) and
service.uuid not in self._standard_services):
if (
isinstance(service.uuid, StandardUUID)
and service.uuid not in self._standard_services
):
self._standard_services.append(service.uuid)
standard = True
elif isinstance(service.uuid, VendorUUID) and service.uuid not in self._vendor_services:
elif (
isinstance(service.uuid, VendorUUID)
and service.uuid not in self._vendor_services
):
self._vendor_services.append(service.uuid)
vendor = True
@ -117,8 +137,10 @@ class BoundServiceList:
data.append(str(service_uuid))
return " ".join(data)
class ServiceList(AdvertisingDataField):
"""Descriptor for a list of Service UUIDs that lazily binds a corresponding BoundServiceList."""
def __init__(self, *, standard_services, vendor_services):
self.standard_services = standard_services
self.vendor_services = vendor_services
@ -142,8 +164,10 @@ class ServiceList(AdvertisingDataField):
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."""
# This is four prefixes, one for each ADT that can carry service UUIDs.
prefix = b"\x01\x02\x01\x03\x01\x06\x01\x07"
services = ServiceList(standard_services=[0x02, 0x03], vendor_services=[0x06, 0x07])
@ -161,8 +185,10 @@ class ProvideServicesAdvertisement(Advertisement):
def matches(cls, entry):
return entry.matches(cls.prefix, all=False)
class SolicitServicesAdvertisement(Advertisement):
"""Advertise what services the device would like to use over a connection."""
# This is two prefixes, one for each ADT that can carry solicited service UUIDs.
prefix = b"\x01\x14\x01\x15"
@ -181,16 +207,19 @@ class ManufacturerData(AdvertisingDataField):
"""Encapsulates manufacturer specific keyed data bytes. The manufacturer is identified by the
company_id and the data is structured like an advertisement with a configurable key
format."""
def __init__(self, obj, *, advertising_data_type=0xff, company_id, key_encoding="B"):
def __init__(
self, obj, *, advertising_data_type=0xFF, company_id, key_encoding="B"
):
self._obj = obj
self._company_id = company_id
self._adt = advertising_data_type
self.data = {}
self.company_id = company_id
encoded_company = struct.pack('<H', company_id)
if 0xff in obj.data_dict:
existing_data = obj.data_dict[0xff]
encoded_company = struct.pack("<H", company_id)
if 0xFF in obj.data_dict:
existing_data = obj.data_dict[0xFF]
if isinstance(existing_data, list):
for existing in existing_data:
if existing.startswith(encoded_company):
@ -203,23 +232,32 @@ class ManufacturerData(AdvertisingDataField):
return 2 + compute_length(self.data, key_encoding=self._key_encoding)
def __bytes__(self):
return (struct.pack('<H', self.company_id) +
encode_data(self.data, key_encoding=self._key_encoding))
return struct.pack("<H", self.company_id) + encode_data(
self.data, key_encoding=self._key_encoding
)
def __str__(self):
hex_data = to_hex(encode_data(self.data, key_encoding=self._key_encoding))
return "<ManufacturerData company_id={:04x} data={} >".format(self.company_id, hex_data)
return "<ManufacturerData company_id={:04x} data={} >".format(
self.company_id, hex_data
)
class ManufacturerDataField:
"""A single piece of data within the manufacturer specific data. The format can be repeated."""
def __init__(self, key, value_format, field_names=None):
self._key = key
self._format = value_format
# TODO: Support format strings that use numbers to repeat a given type. For now, we strip
# numbers because Radio specifies string length with it.
self.element_count = len(value_format.strip("><!=@0123456789").replace("x", ""))
if self.element_count > 1 and (not field_names or len(field_names) != self.element_count):
raise ValueError("Provide field_names when multiple values are in the format")
if self.element_count > 1 and (
not field_names or len(field_names) != self.element_count
):
raise ValueError(
"Provide field_names when multiple values are in the format"
)
self._entry_length = struct.calcsize(value_format)
self.field_names = field_names
@ -248,7 +286,9 @@ class ManufacturerDataField:
def __set__(self, obj, value):
if not obj.mutable:
raise AttributeError()
if isinstance(value, tuple) and (self.element_count == 1 or isinstance(value[0], tuple)):
if isinstance(value, tuple) and (
self.element_count == 1 or isinstance(value[0], tuple)
):
packed = bytearray(self._entry_length * len(value))
for i, entry in enumerate(value):
offset = i * self._entry_length
@ -262,9 +302,11 @@ class ManufacturerDataField:
else:
obj.manufacturer_data.data[self._key] = struct.pack(self._format, *value)
class ServiceData(AdvertisingDataField):
"""Encapsulates service data. It is read as a memoryview which can be manipulated or set as a
bytearray to change the size."""
def __init__(self, service):
if isinstance(service.uuid, StandardUUID):
self._adt = 0x16
@ -289,11 +331,11 @@ class ServiceData(AdvertisingDataField):
if not isinstance(service_data, bytearray):
service_data = bytearray(service_data)
all_service_data[i] = service_data
return memoryview(service_data)[len(self._prefix):]
return memoryview(service_data)[len(self._prefix) :]
if obj.mutable:
service_data = bytearray(self._prefix)
all_service_data.append(service_data)
return memoryview(service_data)[len(self._prefix):]
return memoryview(service_data)[len(self._prefix) :]
# Existing data is a single set of bytes.
elif isinstance(all_service_data, (bytes, bytearray)):
service_data = all_service_data
@ -306,11 +348,10 @@ class ServiceData(AdvertisingDataField):
if not isinstance(service_data, bytearray):
service_data = bytearray(service_data)
obj.data_dict[self._adt] = service_data
return memoryview(service_data)[len(self._prefix):]
return memoryview(service_data)[len(self._prefix) :]
return None
def __set__(self, obj, value):
if not obj.mutable:
raise RuntimeError("Advertisement immutable")

View file

@ -32,6 +32,7 @@ import _bleio
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
class Attribute:
"""Constants describing security levels.
@ -63,6 +64,7 @@ class Attribute:
security_mode: authenticated data signing, without man-in-the-middle protection
"""
# pylint: disable=too-few-public-methods
NO_ACCESS = _bleio.Attribute.NO_ACCESS
OPEN = _bleio.Attribute.OPEN

View file

@ -81,6 +81,7 @@ class Characteristic:
property: clients may write this characteristic; no response will be sent back
"""
BROADCAST = _bleio.Characteristic.BROADCAST
INDICATE = _bleio.Characteristic.INDICATE
NOTIFY = _bleio.Characteristic.NOTIFY
@ -88,10 +89,18 @@ class Characteristic:
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=None, fixed_length=False, initial_value=None):
self.field_name = None # Set by Service during basic binding
def __init__(
self,
*,
uuid=None,
properties=0,
read_perm=Attribute.OPEN,
write_perm=Attribute.OPEN,
max_length=None,
fixed_length=False,
initial_value=None
):
self.field_name = None # Set by Service during basic binding
if uuid:
self.uuid = uuid
@ -130,9 +139,15 @@ class Characteristic:
elif 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, fixed_length=self.fixed_length,
properties=self.properties, read_perm=self.read_perm, write_perm=self.write_perm)
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)
@ -146,16 +161,26 @@ class Characteristic:
bleio_characteristic = service.bleio_characteristics[self.field_name]
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, 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
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
@ -174,15 +199,21 @@ class ComplexCharacteristic:
return characteristic
raise AttributeError("Characteristic not available on remote service")
return _bleio.Characteristic.add_to_service(
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)
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)
setattr(service, self.field_name, bound_object)
return bound_object
class StructCharacteristic(Characteristic):
"""
Data descriptor for a structure with a fixed format.
@ -195,16 +226,30 @@ class StructCharacteristic(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):
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:
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)
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)

View file

@ -33,16 +33,29 @@ from . import StructCharacteristic
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
class FloatCharacteristic(StructCharacteristic):
"""32-bit float"""
def __init__(self, *, uuid=None, properties=0,
read_perm=Attribute.OPEN, write_perm=Attribute.OPEN,
initial_value=None):
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)
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]

View file

@ -33,11 +33,22 @@ from . import StructCharacteristic
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
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):
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:
@ -45,10 +56,14 @@ class IntCharacteristic(StructCharacteristic):
raise ValueError("initial_value out of range")
initial_value = (initial_value,)
super().__init__(format_string, uuid=uuid, properties=properties,
read_perm=read_perm, write_perm=write_perm,
initial_value=initial_value)
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]
@ -58,38 +73,50 @@ class IntCharacteristic(StructCharacteristic):
raise ValueError("out of range")
super().__set__(obj, (value,))
class Int8Characteristic(IntCharacteristic):
"""Int8 number."""
# pylint: disable=too-few-public-methods
def __init__(self, *, min_value=-128, max_value=127, **kwargs):
super().__init__("<b", min_value, max_value, **kwargs)
class Uint8Characteristic(IntCharacteristic):
"""Uint8 number."""
# pylint: disable=too-few-public-methods
def __init__(self, *, min_value=0, max_value=0xff, **kwargs):
def __init__(self, *, min_value=0, max_value=0xFF, **kwargs):
super().__init__("<B", min_value, max_value, **kwargs)
class Int16Characteristic(IntCharacteristic):
"""Int16 number."""
# pylint: disable=too-few-public-methods
def __init__(self, *, min_value=-32768, max_value=32767, **kwargs):
super().__init__("<h", min_value, max_value, **kwargs)
class Uint16Characteristic(IntCharacteristic):
"""Uint16 number."""
# pylint: disable=too-few-public-methods
def __init__(self, *, min_value=0, max_value=0xffff, **kwargs):
def __init__(self, *, min_value=0, max_value=0xFFFF, **kwargs):
super().__init__("<H", min_value, max_value, **kwargs)
class Int32Characteristic(IntCharacteristic):
"""Int32 number."""
# pylint: disable=too-few-public-methods
def __init__(self, *, min_value=-2147483648, max_value=2147483647, **kwargs):
super().__init__("<i", min_value, max_value, **kwargs)
class Uint32Characteristic(IntCharacteristic):
"""Uint32 number."""
# pylint: disable=too-few-public-methods
def __init__(self, *, min_value=0, max_value=0xffffffff, **kwargs):
def __init__(self, *, min_value=0, max_value=0xFFFFFFFF, **kwargs):
super().__init__("<I", min_value, max_value, **kwargs)

View file

@ -36,8 +36,10 @@ from . import ComplexCharacteristic
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
class BoundWriteStream:
"""Writes data out to the peer."""
def __init__(self, bound_characteristic):
self.bound_characteristic = bound_characteristic
@ -46,18 +48,28 @@ class BoundWriteStream:
# We can only write 20 bytes at a time.
offset = 0
while offset < len(buf):
self.bound_characteristic.value = buf[offset:offset+20]
self.bound_characteristic.value = buf[offset : offset + 20]
offset += 20
class StreamOut(ComplexCharacteristic):
"""Output stream from the Service server."""
def __init__(self, *, uuid=None, timeout=1.0, buffer_size=64,
properties=Characteristic.NOTIFY,
read_perm=Attribute.OPEN, write_perm=Attribute.OPEN):
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__(uuid=uuid, properties=properties,
read_perm=read_perm, write_perm=write_perm)
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."""
@ -65,21 +77,34 @@ class StreamOut(ComplexCharacteristic):
# If we're given a remote service then we're the client and need to buffer in.
if service.remote:
bound_characteristic.set_cccd(notify=True)
return _bleio.CharacteristicBuffer(bound_characteristic,
timeout=self._timeout,
buffer_size=self._buffer_size)
return _bleio.CharacteristicBuffer(
bound_characteristic,
timeout=self._timeout,
buffer_size=self._buffer_size,
)
return BoundWriteStream(bound_characteristic)
class StreamIn(ComplexCharacteristic):
"""Input stream into the Service server."""
def __init__(self, *, uuid=None, timeout=1.0, buffer_size=64,
properties=(Characteristic.WRITE | Characteristic.WRITE_NO_RESPONSE),
write_perm=Attribute.OPEN):
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__(uuid=uuid, properties=properties,
read_perm=Attribute.NO_ACCESS,
write_perm=write_perm)
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."""
@ -88,6 +113,6 @@ class StreamIn(ComplexCharacteristic):
if service.remote:
return BoundWriteStream(bound_characteristic)
# We're the server so buffer incoming writes.
return _bleio.CharacteristicBuffer(bound_characteristic,
timeout=self._timeout,
buffer_size=self._buffer_size)
return _bleio.CharacteristicBuffer(
bound_characteristic, timeout=self._timeout, buffer_size=self._buffer_size
)

View file

@ -33,16 +33,28 @@ from . import Characteristic
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
class StringCharacteristic(Characteristic):
"""UTF-8 Encoded string characteristic."""
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,
initial_value=initial_value)
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,
initial_value=initial_value,
)
def __get__(self, obj, cls=None):
return str(super().__get__(obj, cls), "utf-8")
@ -50,12 +62,18 @@ class StringCharacteristic(Characteristic):
def __set__(self, obj, 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, read_perm=Attribute.OPEN):
super().__init__(uuid=uuid, properties=Characteristic.READ,
read_perm=read_perm, write_perm=Attribute.NO_ACCESS,
fixed_length=True)
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):
return str(super().__get__(obj, cls), "utf-8")

View file

@ -32,6 +32,7 @@ from ..characteristics import Characteristic, ComplexCharacteristic
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
class Service:
"""Top level Service class that handles the hard work of binding to a local or remote service.
@ -44,10 +45,13 @@ class Service:
`BLEConnection`. For example, ``connection[UartService]`` will return the UartService
instance for the connection's peer.
"""
def __init__(self, *, service=None, secondary=False, **initial_values):
if service is None:
# pylint: disable=no-member
self.bleio_service = _bleio.Service(self.uuid.bleio_uuid, secondary=secondary)
self.bleio_service = _bleio.Service(
self.uuid.bleio_uuid, secondary=secondary
)
elif not service.remote:
raise ValueError("Can only create services with a remote service or None")
else:
@ -65,8 +69,9 @@ class Service:
if class_attr.startswith("__"):
continue
value = getattr(self.__class__, class_attr)
if (not isinstance(value, Characteristic) and
not isinstance(value, ComplexCharacteristic)):
if not isinstance(value, Characteristic) and not isinstance(
value, ComplexCharacteristic
):
continue
value.field_name = class_attr

View file

@ -36,18 +36,24 @@ from ..uuid import VendorUUID
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
class CircuitPythonUUID(VendorUUID):
"""UUIDs with the CircuitPython base UUID."""
def __init__(self, uuid16):
uuid128 = bytearray("nhtyPtiucriC".encode("utf-8") + b"\x00\x00\xaf\xad")
uuid128[-3] = uuid16 >> 8
uuid128[-4] = uuid16 & 0xff
uuid128[-4] = uuid16 & 0xFF
super().__init__(uuid128)
class CircuitPythonService(Service):
"""Core CircuitPython service that allows for file modification and REPL access.
Unimplemented."""
uuid = CircuitPythonUUID(0x0100)
filename = StringCharacteristic(uuid=CircuitPythonUUID(0x0200),
properties=(Characteristic.READ | Characteristic.WRITE))
filename = StringCharacteristic(
uuid=CircuitPythonUUID(0x0200),
properties=(Characteristic.READ | Characteristic.WRITE),
)
contents = StreamOut(uuid=CircuitPythonUUID(0x0201))

View file

@ -34,20 +34,30 @@ from ..characteristics import Characteristic
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
class MidiIOCharacteristic(Characteristic):
"""Workhorse MIDI Characteristic that carries midi messages both directions. Unimplemented."""
# pylint: disable=too-few-public-methods
uuid = VendorUUID("7772E5DB-3868-4112-A1A9-F2669D106BF3")
def __init__(self, **kwargs):
super().__init__(properties=(Characteristic.NOTIFY |
Characteristic.READ |
Characteristic.WRITE |
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."""
uuid = VendorUUID("03B80E5A-EDE8-4B33-A751-6CE34EC4C700")
io = MidiIOCharacteristic() # pylint: disable=invalid-name
io = MidiIOCharacteristic() # pylint: disable=invalid-name
# pylint: disable=unnecessary-pass
def write(self):

View file

@ -35,6 +35,7 @@ from ..characteristics.stream import StreamOut, StreamIn
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
class UARTService(Service):
"""
Provide UART-like functionality via the Nordic NUS service.
@ -46,12 +47,19 @@ class UARTService(Service):
See ``examples/ble_uart_echo_test.py`` for a usage example.
"""
# pylint: disable=no-member
uuid = VendorUUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
_server_tx = StreamOut(uuid=VendorUUID("6E400003-B5A3-F393-E0A9-E50E24DCCA9E"),
timeout=1.0, buffer_size=64)
_server_rx = StreamIn(uuid=VendorUUID("6E400002-B5A3-F393-E0A9-E50E24DCCA9E"),
timeout=1.0, buffer_size=64)
_server_tx = StreamOut(
uuid=VendorUUID("6E400003-B5A3-F393-E0A9-E50E24DCCA9E"),
timeout=1.0,
buffer_size=64,
)
_server_rx = StreamIn(
uuid=VendorUUID("6E400002-B5A3-F393-E0A9-E50E24DCCA9E"),
timeout=1.0,
buffer_size=64,
)
def __init__(self, service=None):
super().__init__(service=service)

View file

@ -33,6 +33,8 @@ from ..uuid import VendorUUID
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
class SpheroService(Service):
"""Core Sphero Service. Unimplemented."""
uuid = VendorUUID("!!orehpS OOW\x01\x00\x01\x00")

View file

@ -36,41 +36,51 @@ from ...characteristics.int import Uint8Characteristic
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
class AppearanceCharacteristic(StructCharacteristic):
"""What type of device it is"""
# pylint: disable=too-few-public-methods
uuid = StandardUUID(0x2a01)
uuid = StandardUUID(0x2A01)
def __init__(self, **kwargs):
super().__init__("<H", **kwargs)
class GenericAccess(Service):
"""Required service that provides basic device information"""
uuid = StandardUUID(0x1800)
device_name = StringCharacteristic(uuid=StandardUUID(0x2a00))
device_name = StringCharacteristic(uuid=StandardUUID(0x2A00))
appearance = AppearanceCharacteristic()
# privacy_flag
# reconnection_address
# preferred_connection_parameters
class GenericAttribute(Service):
"""Required service that provides notifications when Services change"""
uuid = StandardUUID(0x1801)
# service_changed - indicate only
class BatteryService(Service):
"""Provides battery level information"""
uuid = StandardUUID(0x180f)
uuid = StandardUUID(0x180F)
level = Uint8Characteristic(max_value=100, uuid=StandardUUID(0x2A19))
class CurrentTimeService(Service):
"""Provides the current time."""
uuid = StandardUUID(0x1805)
current_time = StructCharacteristic('<HBBBBBBBB', uuid=StandardUUID(0x2a2b))
current_time = StructCharacteristic("<HBBBBBBBB", uuid=StandardUUID(0x2A2B))
"""A tuple describing the current time:
(year, month, day, hour, minute, second, weekday, subsecond, adjust_reason)"""
local_time_info = StructCharacteristic('<bB', uuid=StandardUUID(0x2a0f))
local_time_info = StructCharacteristic("<bB", uuid=StandardUUID(0x2A0F))
"""A tuple of location information: (timezone, dst_offset)"""
@property
@ -80,4 +90,6 @@ class CurrentTimeService(Service):
"""
year, month, day, hour, minute, second, weekday, _, _ = self.current_time
# Bluetooth weekdays count from 1. struct_time counts from 0.
return time.struct_time((year, month, day, hour, minute, second, weekday - 1, -1, -1))
return time.struct_time(
(year, month, day, hour, minute, second, weekday - 1, -1, -1)
)

View file

@ -38,35 +38,44 @@ from ...characteristics.string import FixedStringCharacteristic
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
class DeviceInfoService(Service):
"""Device information"""
uuid = StandardUUID(0x180a)
model_number = FixedStringCharacteristic(uuid=StandardUUID(0x2a24))
serial_number = FixedStringCharacteristic(uuid=StandardUUID(0x2a25))
firmware_revision = FixedStringCharacteristic(uuid=StandardUUID(0x2a26))
hardware_revision = FixedStringCharacteristic(uuid=StandardUUID(0x2a27))
software_revision = FixedStringCharacteristic(uuid=StandardUUID(0x2a28))
manufacturer = FixedStringCharacteristic(uuid=StandardUUID(0x2a29))
def __init__(self, *,
manufacturer=None,
software_revision=None,
model_number=None,
serial_number=None,
firmware_revision=None,
hardware_revision=None,
service=None):
uuid = StandardUUID(0x180A)
model_number = FixedStringCharacteristic(uuid=StandardUUID(0x2A24))
serial_number = FixedStringCharacteristic(uuid=StandardUUID(0x2A25))
firmware_revision = FixedStringCharacteristic(uuid=StandardUUID(0x2A26))
hardware_revision = FixedStringCharacteristic(uuid=StandardUUID(0x2A27))
software_revision = FixedStringCharacteristic(uuid=StandardUUID(0x2A28))
manufacturer = FixedStringCharacteristic(uuid=StandardUUID(0x2A29))
def __init__(
self,
*,
manufacturer=None,
software_revision=None,
model_number=None,
serial_number=None,
firmware_revision=None,
hardware_revision=None,
service=None
):
if not service:
if model_number is None:
model_number = sys.platform
if serial_number is None:
serial_number = binascii.hexlify(microcontroller.cpu.uid).decode('utf-8') # pylint: disable=no-member
serial_number = binascii.hexlify(
microcontroller.cpu.uid # pylint: disable=no-member
).decode("utf-8")
if firmware_revision is None:
firmware_revision = os.uname().version
super().__init__(manufacturer=manufacturer,
software_revision=software_revision,
model_number=model_number,
serial_number=serial_number,
firmware_revision=firmware_revision,
hardware_revision=hardware_revision,
service=service)
super().__init__(
manufacturer=manufacturer,
software_revision=software_revision,
model_number=model_number,
serial_number=serial_number,
firmware_revision=firmware_revision,
hardware_revision=hardware_revision,
service=service,
)

View file

@ -57,84 +57,84 @@ _APPEARANCE_HID_MOUSE = const(962)
_APPEARANCE_HID_JOYSTICK = const(963)
_APPEARANCE_HID_GAMEPAD = const(964)
#pylint: disable=line-too-long
# pylint: disable=line-too-long
DEFAULT_HID_DESCRIPTOR = (
b'\x05\x01' # Usage Page (Generic Desktop Ctrls)
b'\x09\x06' # Usage (Keyboard)
b'\xA1\x01' # Collection (Application)
b'\x85\x01' # Report ID (1)
b'\x05\x07' # Usage Page (Kbrd/Keypad)
b'\x19\xE0' # Usage Minimum (\xE0)
b'\x29\xE7' # Usage Maximum (\xE7)
b'\x15\x00' # Logical Minimum (0)
b'\x25\x01' # Logical Maximum (1)
b'\x75\x01' # Report Size (1)
b'\x95\x08' # Report Count (8)
b'\x81\x02' # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
b'\x81\x01' # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
b'\x19\x00' # Usage Minimum (\x00)
b'\x29\x65' # Usage Maximum (\x65)
b'\x15\x00' # Logical Minimum (0)
b'\x25\x65' # Logical Maximum (101)
b'\x75\x08' # Report Size (8)
b'\x95\x06' # Report Count (6)
b'\x81\x00' # Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
b'\x05\x08' # Usage Page (LEDs)
b'\x19\x01' # Usage Minimum (Num Lock)
b'\x29\x05' # Usage Maximum (Kana)
b'\x15\x00' # Logical Minimum (0)
b'\x25\x01' # Logical Maximum (1)
b'\x75\x01' # Report Size (1)
b'\x95\x05' # Report Count (5)
b'\x91\x02' # Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
b'\x95\x03' # Report Count (3)
b'\x91\x01' # Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
b'\xC0' # End Collection
b'\x05\x01' # Usage Page (Generic Desktop Ctrls)
b'\x09\x02' # Usage (Mouse)
b'\xA1\x01' # Collection (Application)
b'\x09\x01' # Usage (Pointer)
b'\xA1\x00' # Collection (Physical)
b'\x85\x02' # Report ID (2)
b'\x05\x09' # Usage Page (Button)
b'\x19\x01' # Usage Minimum (\x01)
b'\x29\x05' # Usage Maximum (\x05)
b'\x15\x00' # Logical Minimum (0)
b'\x25\x01' # Logical Maximum (1)
b'\x95\x05' # Report Count (5)
b'\x75\x01' # Report Size (1)
b'\x81\x02' # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
b'\x95\x01' # Report Count (1)
b'\x75\x03' # Report Size (3)
b'\x81\x01' # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
b'\x05\x01' # Usage Page (Generic Desktop Ctrls)
b'\x09\x30' # Usage (X)
b'\x09\x31' # Usage (Y)
b'\x15\x81' # Logical Minimum (-127)
b'\x25\x7F' # Logical Maximum (127)
b'\x75\x08' # Report Size (8)
b'\x95\x02' # Report Count (2)
b'\x81\x06' # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
b'\x09\x38' # Usage (Wheel)
b'\x15\x81' # Logical Minimum (-127)
b'\x25\x7F' # Logical Maximum (127)
b'\x75\x08' # Report Size (8)
b'\x95\x01' # Report Count (1)
b'\x81\x06' # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
b'\xC0' # End Collection
b'\xC0' # End Collection
b'\x05\x0C' # Usage Page (Consumer)
b'\x09\x01' # Usage (Consumer Control)
b'\xA1\x01' # Collection (Application)
b'\x85\x03' # Report ID (3)
b'\x75\x10' # Report Size (16)
b'\x95\x01' # Report Count (1)
b'\x15\x01' # Logical Minimum (1)
b'\x26\x8C\x02' # Logical Maximum (652)
b'\x19\x01' # Usage Minimum (Consumer Control)
b'\x2A\x8C\x02' # Usage Maximum (AC Send)
b'\x81\x00' # Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
b'\xC0' # End Collection
b"\x05\x01" # Usage Page (Generic Desktop Ctrls)
b"\x09\x06" # Usage (Keyboard)
b"\xA1\x01" # Collection (Application)
b"\x85\x01" # Report ID (1)
b"\x05\x07" # Usage Page (Kbrd/Keypad)
b"\x19\xE0" # Usage Minimum (\xE0)
b"\x29\xE7" # Usage Maximum (\xE7)
b"\x15\x00" # Logical Minimum (0)
b"\x25\x01" # Logical Maximum (1)
b"\x75\x01" # Report Size (1)
b"\x95\x08" # Report Count (8)
b"\x81\x02" # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
b"\x81\x01" # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
b"\x19\x00" # Usage Minimum (\x00)
b"\x29\x65" # Usage Maximum (\x65)
b"\x15\x00" # Logical Minimum (0)
b"\x25\x65" # Logical Maximum (101)
b"\x75\x08" # Report Size (8)
b"\x95\x06" # Report Count (6)
b"\x81\x00" # Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
b"\x05\x08" # Usage Page (LEDs)
b"\x19\x01" # Usage Minimum (Num Lock)
b"\x29\x05" # Usage Maximum (Kana)
b"\x15\x00" # Logical Minimum (0)
b"\x25\x01" # Logical Maximum (1)
b"\x75\x01" # Report Size (1)
b"\x95\x05" # Report Count (5)
b"\x91\x02" # Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
b"\x95\x03" # Report Count (3)
b"\x91\x01" # Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
b"\xC0" # End Collection
b"\x05\x01" # Usage Page (Generic Desktop Ctrls)
b"\x09\x02" # Usage (Mouse)
b"\xA1\x01" # Collection (Application)
b"\x09\x01" # Usage (Pointer)
b"\xA1\x00" # Collection (Physical)
b"\x85\x02" # Report ID (2)
b"\x05\x09" # Usage Page (Button)
b"\x19\x01" # Usage Minimum (\x01)
b"\x29\x05" # Usage Maximum (\x05)
b"\x15\x00" # Logical Minimum (0)
b"\x25\x01" # Logical Maximum (1)
b"\x95\x05" # Report Count (5)
b"\x75\x01" # Report Size (1)
b"\x81\x02" # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
b"\x95\x01" # Report Count (1)
b"\x75\x03" # Report Size (3)
b"\x81\x01" # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
b"\x05\x01" # Usage Page (Generic Desktop Ctrls)
b"\x09\x30" # Usage (X)
b"\x09\x31" # Usage (Y)
b"\x15\x81" # Logical Minimum (-127)
b"\x25\x7F" # Logical Maximum (127)
b"\x75\x08" # Report Size (8)
b"\x95\x02" # Report Count (2)
b"\x81\x06" # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
b"\x09\x38" # Usage (Wheel)
b"\x15\x81" # Logical Minimum (-127)
b"\x25\x7F" # Logical Maximum (127)
b"\x75\x08" # Report Size (8)
b"\x95\x01" # Report Count (1)
b"\x81\x06" # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
b"\xC0" # End Collection
b"\xC0" # End Collection
b"\x05\x0C" # Usage Page (Consumer)
b"\x09\x01" # Usage (Consumer Control)
b"\xA1\x01" # Collection (Application)
b"\x85\x03" # Report ID (3)
b"\x75\x10" # Report Size (16)
b"\x95\x01" # Report Count (1)
b"\x15\x01" # Logical Minimum (1)
b"\x26\x8C\x02" # Logical Maximum (652)
b"\x19\x01" # Usage Minimum (Consumer Control)
b"\x2A\x8C\x02" # Usage Maximum (AC Send)
b"\x81\x00" # Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
b"\xC0" # End Collection
# b'\x05\x01' # Usage Page (Generic Desktop Ctrls)
# b'\x09\x05' # Usage (Game Pad)
# b'\xA1\x01' # Collection (Application)
@ -160,7 +160,7 @@ DEFAULT_HID_DESCRIPTOR = (
# b'\xC0' # End Collection
)
"""Default HID descriptor: provides mouse, keyboard, and consumer control devices."""
#pylint: enable=line-too-long
# pylint: enable=line-too-long
# Boot keyboard and mouse not currently supported.
@ -173,54 +173,74 @@ _REPORT_TYPE_INPUT = const(1)
_REPORT_TYPE_OUTPUT = const(2)
# Boot Protocol mode not currently implemented
_PROTOCOL_MODE_BOOT = b'\x00'
_PROTOCOL_MODE_REPORT = b'\x01'
_PROTOCOL_MODE_BOOT = b"\x00"
_PROTOCOL_MODE_REPORT = b"\x01"
class ReportIn:
"""A single HID report that transmits HID data into a client."""
uuid = StandardUUID(_REPORT_UUID_NUM)
def __init__(self, service, report_id, usage_page, usage, *, max_length):
self._characteristic = _bleio.Characteristic.add_to_service(
service.bleio_service,
self.uuid.bleio_uuid,
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
self.usage = usage
_bleio.Descriptor.add_to_characteristic(
self._characteristic, _REPORT_REF_DESCR_UUID,
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):
"""Send a report to the peers"""
self._characteristic.value = report
class ReportOut:
"""A single HID report that receives HID data from a client."""
# pylint: disable=too-few-public-methods
uuid = StandardUUID(_REPORT_UUID_NUM)
def __init__(self, service, report_id, usage_page, usage, *, max_length):
self._characteristic = _bleio.Characteristic.add_to_service(
service.bleio_service,
self.uuid.bleio_uuid,
read_perm=Attribute.ENCRYPT_NO_MITM,
write_perm=Attribute.NO_ACCESS,
max_length=max_length,
fixed_length=True,
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
self.usage = usage
_bleio.Descriptor.add_to_characteristic(
self._characteristic, _REPORT_REF_DESCR_UUID,
read_perm=Attribute.ENCRYPT_NO_MITM, write_perm=Attribute.NO_ACCESS,
initial_value=struct.pack('<BB', self._report_id, _REPORT_TYPE_OUTPUT))
self._characteristic,
_REPORT_REF_DESCR_UUID,
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):
"""Send a report to the peers"""
self._characteristic.value = report
class ReportOut:
"""A single HID report that receives HID data from a client."""
# pylint: disable=too-few-public-methods
uuid = StandardUUID(_REPORT_UUID_NUM)
def __init__(self, service, report_id, usage_page, usage, *, max_length):
self._characteristic = _bleio.Characteristic.add_to_service(
service.bleio_service,
self.uuid.bleio_uuid,
max_length=max_length,
fixed_length=True,
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
self.usage = usage
_bleio.Descriptor.add_to_characteristic(
self._characteristic,
_REPORT_REF_DESCR_UUID,
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)
_ITEM_TYPE_GLOBAL = const(1)
@ -232,6 +252,7 @@ _MAIN_ITEM_TAG_INPUT = const(0b1000)
_MAIN_ITEM_TAG_OUTPUT = const(0b1001)
_MAIN_ITEM_TAG_FEATURE = const(0b1011)
class HIDService(Service):
"""
Provide devices for HID over BLE.
@ -245,55 +266,70 @@ class HIDService(Service):
hid = HIDServer()
"""
uuid = StandardUUID(0x1812)
boot_keyboard_in = Characteristic(uuid=StandardUUID(0x2A22),
properties=(Characteristic.READ |
Characteristic.NOTIFY),
read_perm=Attribute.ENCRYPT_NO_MITM,
write_perm=Attribute.NO_ACCESS,
max_length=8, fixed_length=True)
boot_keyboard_in = Characteristic(
uuid=StandardUUID(0x2A22),
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=(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)
boot_keyboard_out = Characteristic(
uuid=StandardUUID(0x2A32),
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=(Characteristic.READ |
Characteristic.WRITE_NO_RESPONSE),
read_perm=Attribute.OPEN,
write_perm=Attribute.OPEN,
initial_value=1, max_value=1)
protocol_mode = Uint8Characteristic(
uuid=StandardUUID(0x2A4E),
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)"""
# bcdHID (version), bCountryCode (0 not localized), Flags: RemoteWake, NormallyConnectable
# bcd1.1, country = 0, flag = normal connect
# TODO: Make this a struct.
hid_information = Characteristic(uuid=StandardUUID(0x2A4A),
properties=Characteristic.READ,
read_perm=Attribute.ENCRYPT_NO_MITM,
write_perm=Attribute.NO_ACCESS,
initial_value=b'\x01\x01\x00\x02')
hid_information = Characteristic(
uuid=StandardUUID(0x2A4A),
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=Characteristic.READ,
read_perm=Attribute.ENCRYPT_NO_MITM,
write_perm=Attribute.NO_ACCESS,
fixed_length=True)
report_map = Characteristic(
uuid=StandardUUID(0x2A4B),
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=Characteristic.WRITE_NO_RESPONSE,
read_perm=Attribute.NO_ACCESS,
write_perm=Attribute.ENCRYPT_NO_MITM,
max_value=1)
suspended = Uint8Characteristic(
uuid=StandardUUID(0x2A4C),
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)."""
def __init__(self, hid_descriptor=DEFAULT_HID_DESCRIPTOR, service=None):
@ -316,20 +352,24 @@ class HIDService(Service):
i = 0
while i < len(hid_descriptor):
b = hid_descriptor[i]
tag = (b & 0xf0) >> 4
tag = (b & 0xF0) >> 4
_type = (b & 0b1100) >> 2
size = b & 0b11
size = 4 if size == 3 else size
i += 1
data = hid_descriptor[i:i+size]
data = hid_descriptor[i : i + size]
if _type == _ITEM_TYPE_GLOBAL:
global_table[tag] = data
elif _type == _ITEM_TYPE_MAIN:
if tag == _MAIN_ITEM_TAG_START_COLLECTION:
collections.append({"type": data,
"locals": list(local_table),
"globals": list(global_table),
"mains": []})
collections.append(
{
"type": data,
"locals": list(local_table),
"globals": list(global_table),
"mains": [],
}
)
elif tag == _MAIN_ITEM_TAG_END_COLLECTION:
collection = collections.pop()
# This is a top level collection if the collections list is now empty.
@ -338,13 +378,21 @@ class HIDService(Service):
else:
collections[-1]["mains"].append(collection)
elif tag == _MAIN_ITEM_TAG_INPUT:
collections[-1]["mains"].append({"tag": "input",
"locals": list(local_table),
"globals": list(global_table)})
collections[-1]["mains"].append(
{
"tag": "input",
"locals": list(local_table),
"globals": list(global_table),
}
)
elif tag == _MAIN_ITEM_TAG_OUTPUT:
collections[-1]["mains"].append({"tag": "output",
"locals": list(local_table),
"globals": list(global_table)})
collections[-1]["mains"].append(
{
"tag": "output",
"locals": list(local_table),
"globals": list(global_table),
}
)
else:
raise RuntimeError("Unsupported main item in HID descriptor")
local_table = [None] * 3
@ -359,7 +407,9 @@ class HIDService(Service):
if "type" in main:
get_report_info(main, reports)
else:
report_size, report_id, report_count = [x[0] for x in main["globals"][7:10]]
report_size, report_id, report_count = [
x[0] for x in main["globals"][7:10]
]
if report_id not in reports:
reports[report_id] = {"input_size": 0, "output_size": 0}
if main["tag"] == "input":
@ -367,24 +417,33 @@ class HIDService(Service):
elif main["tag"] == "output":
reports[report_id]["output_size"] += report_size * report_count
for collection in top_level_collections:
if collection["type"][0] != 1:
raise NotImplementedError("Only Application top level collections supported.")
raise NotImplementedError(
"Only Application top level collections supported."
)
usage_page = collection["globals"][0][0]
usage = collection["locals"][0][0]
reports = {}
get_report_info(collection, reports)
if len(reports) > 1:
raise NotImplementedError("Only one report id per Application collection supported")
raise NotImplementedError(
"Only one report id per Application collection supported"
)
report_id, report = list(reports.items())[0]
output_size = report["output_size"]
if output_size > 0:
self.devices.append(ReportOut(self, report_id, usage_page, usage,
max_length=output_size // 8))
self.devices.append(
ReportOut(
self, report_id, usage_page, usage, max_length=output_size // 8
)
)
input_size = reports[report_id]["input_size"]
if input_size > 0:
self.devices.append(ReportIn(self, report_id, usage_page, usage,
max_length=input_size // 8))
self.devices.append(
ReportIn(
self, report_id, usage_page, usage, max_length=input_size // 8
)
)

View file

@ -32,8 +32,10 @@ import _bleio
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"
class UUID:
"""Top level UUID"""
# TODO: Make subclassing _bleio.UUID work so we can just use it directly.
# pylint: disable=no-member
def __hash__(self):
@ -60,16 +62,20 @@ class UUID:
"""Packs the UUID into the buffer at the given offset."""
self.bleio_uuid.pack_into(buffer, offset=offset)
class StandardUUID(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]
self.bleio_uuid = _bleio.UUID(uuid16)
self.size = 16
class VendorUUID(UUID):
"""Vendor defined, 128-bit UUID."""
def __init__(self, uuid128):
self.bleio_uuid = _bleio.UUID(uuid128)
self.size = 128

View file

@ -2,7 +2,8 @@
import os
import sys
sys.path.insert(0, os.path.abspath('..'))
sys.path.insert(0, os.path.abspath(".."))
# -- General configuration ------------------------------------------------
@ -10,10 +11,10 @@ sys.path.insert(0, os.path.abspath('..'))
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.intersphinx',
'sphinx.ext.napoleon',
'sphinx.ext.todo',
"sphinx.ext.autodoc",
"sphinx.ext.intersphinx",
"sphinx.ext.napoleon",
"sphinx.ext.todo",
]
# TODO: Please Read!
@ -24,30 +25,32 @@ autodoc_mock_imports = ["board", "microcontroller"]
autodoc_member_order = "bysource"
add_module_names = False
intersphinx_mapping = {'python': ('https://docs.python.org/3.4', None),
'CircuitPython': ('https://circuitpython.readthedocs.io/en/latest/', None)}
intersphinx_mapping = {
"python": ("https://docs.python.org/3.4", None),
"CircuitPython": ("https://circuitpython.readthedocs.io/en/latest/", None),
}
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]
source_suffix = '.rst'
source_suffix = ".rst"
# The master toctree document.
master_doc = 'index'
master_doc = "index"
# General information about the project.
project = u'Adafruit ble Library'
copyright = u'2019 Dan Halbert for Adafruit Industries'
author = u'Dan Halbert'
project = u"Adafruit ble Library"
copyright = u"2019 Dan Halbert for Adafruit Industries"
author = u"Dan Halbert"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = u'1.0'
version = u"1.0"
# The full version, including alpha/beta/rc tags.
release = u'1.0'
release = u"1.0"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@ -59,7 +62,7 @@ language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', '.env', 'CODE_OF_CONDUCT.md']
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", ".env", "CODE_OF_CONDUCT.md"]
# The reST default role (used for this markup: `text`) to use for all
# documents.
@ -71,7 +74,7 @@ default_role = "any"
add_function_parentheses = True
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = "sphinx"
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
@ -86,59 +89,62 @@ napoleon_numpy_docstring = False
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
on_rtd = os.environ.get("READTHEDOCS", None) == "True"
if not on_rtd: # only import and set the theme if we're building docs locally
try:
import sphinx_rtd_theme
html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), '.']
html_theme = "sphinx_rtd_theme"
html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), "."]
except:
html_theme = 'default'
html_theme_path = ['.']
html_theme = "default"
html_theme_path = ["."]
else:
html_theme_path = ['.']
html_theme_path = ["."]
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_static_path = ["_static"]
# The name of an image file (relative to this directory) to use as a favicon of
# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#
html_favicon = '_static/favicon.ico'
html_favicon = "_static/favicon.ico"
# Output file base name for HTML help builder.
htmlhelp_basename = 'AdafruitBleLibrarydoc'
htmlhelp_basename = "AdafruitBleLibrarydoc"
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'AdafruitbleLibrary.tex', u'Adafruit BLE Library Documentation',
author, 'manual'),
(
master_doc,
"AdafruitbleLibrary.tex",
u"Adafruit BLE Library Documentation",
author,
"manual",
),
]
# -- Options for manual page output ---------------------------------------
@ -146,8 +152,13 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'Adafruitblelibrary', u'Adafruit BLE Library Documentation',
[author], 1)
(
master_doc,
"Adafruitblelibrary",
u"Adafruit BLE Library Documentation",
[author],
1,
)
]
# -- Options for Texinfo output -------------------------------------------
@ -156,7 +167,13 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'AdafruitbleLibrary', u'Adafruit BLE Library Documentation',
author, 'AdafruitbleLibrary', 'One line description of project.',
'Miscellaneous'),
(
master_doc,
"AdafruitbleLibrary",
u"Adafruit BLE Library Documentation",
author,
"AdafruitbleLibrary",
"One line description of project.",
"Miscellaneous",
),
]

View file

@ -31,5 +31,5 @@ while True:
while ble.connected:
print((scale(light.value), thermistor.temperature))
uart_server.write('{},{}\n'.format(scale(light.value), thermistor.temperature))
uart_server.write("{},{}\n".format(scale(light.value), thermistor.temperature))
time.sleep(0.1)

View file

@ -14,16 +14,18 @@ from adafruit_ble import BLERadio
from adafruit_ble.advertising.adafruit import AdafruitColor
# The color pickers will cycle through this list with buttons A and B.
color_options = [0x110000,
0x111100,
0x001100,
0x001111,
0x000011,
0x110011,
0x111111,
0x221111,
0x112211,
0x111122]
color_options = [
0x110000,
0x111100,
0x001100,
0x001111,
0x000011,
0x110011,
0x111111,
0x221111,
0x112211,
0x111122,
]
ble = BLERadio()
@ -85,7 +87,7 @@ while True:
closest_rssi = entry.rssi
closest_last_time = now
discrete_strength = min((100 + entry.rssi) // 5, 10)
#print(entry.rssi, discrete_strength)
# print(entry.rssi, discrete_strength)
neopixels.fill(0x000000)
for i in range(0, discrete_strength):
neopixels[i] = entry.color

View file

@ -17,12 +17,14 @@ import neopixel
from adafruit_bluefruit_connect.color_packet import ColorPacket
def scale(value):
"""Scale an value from (acceleration range) to 0-255 (RGB range)"""
value = abs(value)
value = max(min(19.6, value), 0)
return int(value / 19.6 * 255)
i2c = busio.I2C(board.ACCELEROMETER_SCL, board.ACCELEROMETER_SDA)
int1 = digitalio.DigitalInOut(board.ACCELEROMETER_INTERRUPT)
accelerometer = adafruit_lis3dh.LIS3DH_I2C(i2c, address=0x19, int1=int1)
@ -62,7 +64,7 @@ while True:
except OSError:
try:
uart_connection.disconnect()
except: # pylint: disable=bare-except
except: # pylint: disable=bare-except
pass
uart_connection = None
time.sleep(0.3)

View file

@ -18,6 +18,7 @@ NUM_PIXELS = 10
np = neopixel.NeoPixel(board.NEOPIXEL, NUM_PIXELS, brightness=0.1)
next_pixel = 0
def mod(i):
"""Wrap i to modulus NUM_PIXELS."""
return i % NUM_PIXELS

View file

@ -16,8 +16,9 @@ from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
# Use default HID descriptor
hid = HIDService()
device_info = DeviceInfoService(software_revision=adafruit_ble.__version__,
manufacturer="Adafruit Industries")
device_info = DeviceInfoService(
software_revision=adafruit_ble.__version__, manufacturer="Adafruit Industries"
)
advertisement = ProvideServicesAdvertisement(hid)
advertisement.appearance = 961
scan_response = Advertisement()

View file

@ -10,7 +10,9 @@ from adafruit_ble.services.nordic import UARTService
ble = BLERadio()
while True:
while ble.connected and any(UARTService in connection for connection in ble.connections):
while ble.connected and any(
UARTService in connection for connection in ble.connections
):
for connection in ble.connections:
if UARTService not in connection:
continue

View file

@ -6,6 +6,7 @@ https://github.com/pypa/sampleproject
"""
from setuptools import setup, find_packages
# To use a consistent encoding
from codecs import open
from os import path
@ -13,48 +14,36 @@ from os import path
here = path.abspath(path.dirname(__file__))
# Get the long description from the README file
with open(path.join(here, 'README.rst'), encoding='utf-8') as f:
with open(path.join(here, "README.rst"), encoding="utf-8") as f:
long_description = f.read()
setup(
name='adafruit-circuitpython-ble',
name="adafruit-circuitpython-ble",
use_scm_version=True,
setup_requires=['setuptools_scm'],
description='Bluetooth Low Energy (BLE) library for CircuitPython',
setup_requires=["setuptools_scm"],
description="Bluetooth Low Energy (BLE) library for CircuitPython",
long_description=long_description,
long_description_content_type='text/x-rst',
long_description_content_type="text/x-rst",
# The project's main homepage.
url='https://github.com/adafruit/Adafruit_CircuitPython_BLE',
url="https://github.com/adafruit/Adafruit_CircuitPython_BLE",
# Author details
author='Adafruit Industries',
author_email='circuitpython@adafruit.com',
install_requires=[
'adafruit-blinka',
'adafruit-blinka-bleio'
],
author="Adafruit Industries",
author_email="circuitpython@adafruit.com",
install_requires=["adafruit-blinka", "adafruit-blinka-bleio"],
# Choose your license
license='MIT',
license="MIT",
# See https://pypi.python.org/pypi?%3Aaction=list_classifiers
classifiers=[
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',
'Topic :: Software Development :: Libraries',
'Topic :: System :: Hardware',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Topic :: Software Development :: Libraries",
"Topic :: System :: Hardware",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
],
# What does your project relate to?
keywords='adafruit blinka circuitpython micropython ble bluetooth',
keywords="adafruit blinka circuitpython micropython ble bluetooth",
packages=find_packages(include=["adafruit_ble", "adafruit_ble.*"]),
)