Skip to content

Add JSON characteristic #162

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Apr 23, 2022
59 changes: 59 additions & 0 deletions adafruit_ble/characteristics/json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# SPDX-FileCopyrightText: 2022 Mark Raleson
#
# SPDX-License-Identifier: MIT

"""
`json`
====================================================

This module provides Json characteristic.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make this grammatical, and expand on what is going on a bit more.


"""

import json
from . import Attribute
from . import Characteristic

__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE.git"


class JsonCharacteristic(Characteristic):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you make this a subclass of StringCharacteristic, that would seem to save a few encoding/decoding steps?

Copy link
Contributor Author

@mraleson mraleson Apr 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did this initially, but because of the packing/unpacking of initial_value in init and also in the setter getter inheriting from StringCharacteristic it ended up not really saving any typing. It also added an additional inheritance layer that hid attributes like max_length.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use JSON instead of Json. The former is typical in existing Python libraries, including those that are included in CPython.

Suggested change
class JsonCharacteristic(Characteristic):
class JSONCharacteristic(Characteristic):

"""Json string characteristic for JSON serializable values of a limited size (max_length)."""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"""Json string characteristic for JSON serializable values of a limited size (max_length)."""
"""JSON string characteristic for JSON serializable values of a limited size (max_length)."""


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=512,
fixed_length=False,
initial_value=self.pack(initial_value),
)

@staticmethod
def pack(value):
"""Converts a JSON serializable python value into a utf-8 encoded JSON string."""
return json.dumps(value).encode("utf-8")

@staticmethod
def unpack(value):
"""Converts a utf-8 encoded JSON string into a python value."""
return json.loads(str(value, "utf-8"))

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

def __set__(self, obj, value):
super().__set__(obj, self.pack(value))
32 changes: 32 additions & 0 deletions examples/ble_json_central.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# SPDX-FileCopyrightText: 2020 Mark Raleson
#
# SPDX-License-Identifier: MIT

# Read sensor readings from peripheral BLE device using JSON characteristic.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix grammar -> "a JSON characteristic" , or similar.


from adafruit_ble import BLERadio
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from ble_json_service import SensorService


ble = BLERadio()
connection = None

while True:

if not connection:
print("Scanning for BLE device advertising our sensor service...")
for adv in ble.start_scan(ProvideServicesAdvertisement):
if SensorService in adv.services:
connection = ble.connect(adv)
print("Connected")
break
ble.stop_scan()

if connection and connection.connected:
service = connection[SensorService]
service.settings = {
'unit': 'celsius' # 'fahrenheit'
}
while connection.connected:
print('Sensors: ', service.sensors)
49 changes: 49 additions & 0 deletions examples/ble_json_peripheral.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# SPDX-FileCopyrightText: 2020 Mark Raleson
#
# SPDX-License-Identifier: MIT

# Provide readable sensor values and writable settings to connected devices via Json characteristic.

import time
import random
from adafruit_ble import BLERadio
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from ble_json_service import SensorService


# Create BLE radio, custom service, and advertisement.
ble = BLERadio()
service = SensorService()
advertisement = ProvideServicesAdvertisement(service)

# Function to get some fake weather sensor readings for this example in the desired unit.
def measure(unit):
temperature = random.uniform(0.0, 10.0)
humidity = random.uniform(0.0, 100.0)
if unit == 'fahrenheit':
temperature = (temperature * 9.0 / 5.0) + 32.0
return {
'temperature': temperature,
'humidity': humidity
}

# Advertise until another device connects, when a device connects, provide sensor data.
while True:
print('Advertise services')
ble.stop_advertising() # you need to do this to stop any persistent old advertisement
ble.start_advertising(advertisement)

print("Waiting for connection...")
while not ble.connected:
pass

print("Connected")
while ble.connected:
settings = service.settings
measurement = measure(settings.get('unit', 'celsius'))
service.sensors = measurement
print('Settings: ', settings)
print('Sensors: ', measurement)
time.sleep(.25)

print('Disconnected')
30 changes: 30 additions & 0 deletions examples/ble_json_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from adafruit_ble.uuid import VendorUUID
from adafruit_ble.services import Service
from adafruit_ble.characteristics import Characteristic
from adafruit_ble.characteristics.json import JsonCharacteristic


# A custom service with two Json characteristics for this device. The "sensors" characteristic
# provides updated sensor values for any connected device to read. The "settings" characteristic
# can be changed by any connected device to update the peripheral's settings. The UUID of your
# service can be any valid random uuid (some BLE UUID's are reserved).
# NOTE: Json data is limited by characteristic max_length of 512 byes.
class SensorService(Service):
uuid = VendorUUID("51ad213f-e568-4e35-84e4-67af89c79ef0")

settings = JsonCharacteristic(
uuid=VendorUUID("e077bdec-f18b-4944-9e9e-8b3a815162b4"),
properties=Characteristic.READ | Characteristic.WRITE,
initial_value={
'unit': 'celsius'
}
)

sensors = JsonCharacteristic(
uuid=VendorUUID("528ff74b-fdb8-444c-9c64-3dd5da4135ae"),
properties=Characteristic.READ,
)

def __init__(self, service=None):
super().__init__(service=service)
self.connectable = True