diff --git a/adafruit_ble/characteristics/json.py b/adafruit_ble/characteristics/json.py new file mode 100644 index 0000000..9ac68b5 --- /dev/null +++ b/adafruit_ble/characteristics/json.py @@ -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)) diff --git a/examples/ble_json_central.py b/examples/ble_json_central.py new file mode 100644 index 0000000..51a04e8 --- /dev/null +++ b/examples/ble_json_central.py @@ -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) diff --git a/examples/ble_json_peripheral.py b/examples/ble_json_peripheral.py new file mode 100644 index 0000000..67337cf --- /dev/null +++ b/examples/ble_json_peripheral.py @@ -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") diff --git a/examples/ble_json_service.py b/examples/ble_json_service.py new file mode 100644 index 0000000..6351564 --- /dev/null +++ b/examples/ble_json_service.py @@ -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