Skip to content

Commit

Permalink
Merge pull request #1 from tannewt/polish
Browse files Browse the repository at this point in the history
Polish up for release
  • Loading branch information
dhalbert authored Feb 19, 2020
2 parents 187004b + 7302257 commit c9fdad7
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 125 deletions.
33 changes: 27 additions & 6 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,6 @@ This is easily achieved by downloading

Installing from PyPI
=====================
.. note:: This library is not available on PyPI yet. Install documentation is included
as a standard element. Stay tuned for PyPI availability!

.. todo:: Remove the above note if PyPI version is/will be available at time of release.
If the library is not planned for PyPI, remove the entire 'Installing from PyPI' section.

On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from
PyPI <https://pypi.org/project/adafruit-circuitpython-ble_eddystone/>`_. To install for current user:
Expand All @@ -59,7 +54,33 @@ To install in a virtual environment in your current project:
Usage Example
=============

.. todo:: Add a quick, simple example. It and other examples should live in the examples folder and be included in docs/examples.rst.
.. code-block:: python
"""This example broadcasts our Mac Address as our Eddystone ID and a link to the Adafruit Discord
server."""
import time
import adafruit_ble
from adafruit_ble_eddystone import uid, url
radio = adafruit_ble.BLERadio()
# Reuse the BLE address as our Eddystone instance id.
eddystone_uid = uid.EddystoneUID(radio.address_bytes)
eddystone_url = url.EddystoneURL("https://adafru.it/discord")
while True:
# Alternate between advertising our ID and our URL.
radio.start_advertising(eddystone_uid)
time.sleep(0.5)
radio.stop_advertising()
radio.start_advertising(eddystone_url)
time.sleep(0.5)
radio.stop_advertising()
time.sleep(4)
Contributing
============
Expand Down
31 changes: 23 additions & 8 deletions adafruit_ble_eddystone/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,62 +38,77 @@
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE_Eddystone.git"


class _EddystoneService:
"""Placeholder service. Not implemented."""

# pylint: disable=too-few-public-methods
uuid = StandardUUID(0xfeaa)
uuid = StandardUUID(0xFEAA)


class _EddystoneFrame(ServiceData):
"""Top level advertising data field that adds the field type to bytearrays set."""

def __init__(self):
super().__init__(_EddystoneService)

def __get__(self, obj, cls):
if obj is None:
return self
return super().__get__(obj, cls)[1:]

def __set__(self, obj, value):
return super().__set__(obj, obj.frame_type + value)


class EddystoneFrameBytes:
"""Extracts and manipulates a byte range from an EddystoneAdvertisement. For library use only.
If length is None, then the byte range must be at the end of the frame.
"""

def __init__(self, *, length=None, offset=0):
self._length = length
self._offset = offset

def __get__(self, obj, cls):
if obj is None:
return self
if self._length is not None:
return obj._eddystone_frame[self._offset:self._offset+self._length]
return obj._eddystone_frame[self._offset:]
return obj.eddystone_frame[self._offset : self._offset + self._length]
return obj.eddystone_frame[self._offset :]

def __set__(self, obj, value):
if self._length is not None:
if self._length != len(value):
raise ValueError("Value length does not match")
obj._eddystone_frame[self._offset:self._offset+self._length] = value
obj.eddystone_frame[self._offset : self._offset + self._length] = value
else:
obj._eddystone_frame = obj._eddystone_frame[:self._offset] + value
obj.eddystone_frame = obj.eddystone_frame[: self._offset] + value


class EddystoneFrameStruct(EddystoneFrameBytes):
"""Packs and unpacks a single value from a byte range. For library use only."""

def __init__(self, fmt, *, offset=0):
self._format = fmt
super().__init__(offset=offset, length=struct.calcsize(self._format))

def __get__(self, obj, cls):
if obj is None:
return self
return struct.unpack(self._format, super().__get__(obj, cls))[0]

def __set__(self, obj, value):
super().__set__(obj, struct.pack(self._format, value))


class EddystoneAdvertisement(Advertisement):
"""Top level Eddystone advertisement that manages frame type. For library use only."""

# Subclasses must provide `prefix`.
services = ServiceList(standard_services=[0x03], vendor_services=[0x07])
_eddystone_frame = _EddystoneFrame()
eddystone_frame = _EddystoneFrame()

def __init__(self, *, minimum_size=None):
super().__init__()
Expand All @@ -104,8 +119,8 @@ def __init__(self, *, minimum_size=None):
self.frame_type = bytearray(1)
# Frame type is in the prefix.
self.frame_type[0] = self.prefix[-1]
if not self._eddystone_frame:
self._eddystone_frame = bytearray(minimum_size)
if not self.eddystone_frame:
self.eddystone_frame = bytearray(minimum_size)

@classmethod
def matches(cls, entry):
Expand Down
1 change: 1 addition & 0 deletions adafruit_ble_eddystone/uid.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class EddystoneUID(EddystoneAdvertisement):
:param bytes namespace_id: namespace component of the id. 6 bytes long
:param int tx_power: TX power at the beacon
"""

prefix = b"\x03\x03\xaa\xfe\x04\x16\xaa\xfe\x00"

tx_power = EddystoneFrameStruct("<B", offset=0)
Expand Down
47 changes: 22 additions & 25 deletions adafruit_ble_eddystone/url.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,61 +34,57 @@
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE_Eddystone.git"

# These prefixes are replaced with a single one-byte scheme number.
_URL_SCHEMES = (
b'http://www.',
b'https://www.',
b'http://',
b'https://'
)
_URL_SCHEMES = (b"http://www.", b"https://www.", b"http://", b"https://")

# These common domains are replaced with a single non-printing byte.
# Byte value is 0-6 for these with a '/' suffix.
# Byte value is 7-13 for these without the '/' suffix.
_SUBSTITUTIONS = (
b'.com',
b'.org',
b'.edu'
b'.net',
b'.info',
b'.biz',
b'.gov',
b".com",
b".org",
b".edu",
b".net",
b".info",
b".biz",
b".gov",
)


class _EncodedEddystoneUrl(EddystoneFrameBytes):
"""Packs and unpacks an encoded url"""
def __init__(self, *, offset=0):
super().__init__(offset=offset)

def __get__(self, obj, cls):
if obj is None:
return self
short_url = bytes(super().__get__(obj, cls))

if short_url[0] < len(_URL_SCHEMES):
short_url = _URL_SCHEMES[short_url[0]] + short_url[1:]

for code, subst in enumerate(_SUBSTITUTIONS):
code = bytes(chr(code), 'ascii')
short_url = short_url.replace(code, subst + b'/')
code = bytes(chr(code), "ascii")
short_url = short_url.replace(code, subst + b"/")
for code, subst in enumerate(_SUBSTITUTIONS, 7):
code = bytes(chr(code), 'ascii')
code = bytes(chr(code), "ascii")
short_url = short_url.replace(code, subst)

return str(short_url, 'ascii')
return str(short_url, "ascii")

def __set__(self, obj, url):
short_url = None
url = bytes(url, 'ascii')
url = bytes(url, "ascii")
for idx, prefix in enumerate(_URL_SCHEMES):
if url.startswith(prefix):
short_url = url[len(prefix):]
short_url = bytes(chr(idx), 'ascii') + short_url
short_url = url[len(prefix) :]
short_url = bytes(chr(idx), "ascii") + short_url
break
if not short_url:
raise ValueError("url does not start with one of: ", _URL_SCHEMES)
for code, subst in enumerate(_SUBSTITUTIONS):
code = bytes(chr(code), 'ascii')
short_url = short_url.replace(subst + b'/', code)
code = bytes(chr(code), "ascii")
short_url = short_url.replace(subst + b"/", code)
for code, subst in enumerate(_SUBSTITUTIONS, 7):
code = bytes(chr(code), 'ascii')
code = bytes(chr(code), "ascii")
short_url = short_url.replace(subst, code)

super().__set__(obj, short_url)
Expand All @@ -99,6 +95,7 @@ class EddystoneURL(EddystoneAdvertisement):
:param str url: Target url
:param int tx_power: TX power in dBm"""

prefix = b"\x03\x03\xaa\xfe\x04\x16\xaa\xfe\x10"
tx_power = EddystoneFrameStruct("<B", offset=0)
"""TX power in dBm"""
Expand Down
Loading

0 comments on commit c9fdad7

Please sign in to comment.