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 a JSON characteristic for reading/writing JSON serializable Python values.

"""

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):
"""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))
30 changes: 30 additions & 0 deletions examples/ble_json_central.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# SPDX-FileCopyrightText: 2020 Mark Raleson
#
# SPDX-License-Identifier: MIT

# Read sensor readings from peripheral BLE device using a JSON characteristic.

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


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)
47 changes: 47 additions & 0 deletions examples/ble_json_peripheral.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# 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 ble_json_service import SensorService
from adafruit_ble import BLERadio
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement


# 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(0.25)

print("Disconnected")
36 changes: 36 additions & 0 deletions examples/ble_json_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# SPDX-FileCopyrightText: 2020 Mark Raleson
#
# SPDX-License-Identifier: MIT

# Read sensor readings from peripheral BLE device using a JSON characteristic.

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):
# pylint: disable=too-few-public-methods

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