From cb33c86f43a937d5552764d269f364c301ea6144 Mon Sep 17 00:00:00 2001 From: mraleson Date: Sat, 16 Apr 2022 13:27:35 -0600 Subject: [PATCH 01/11] Add JSON characteristic --- adafruit_ble/characteristics/json.py | 53 ++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 adafruit_ble/characteristics/json.py diff --git a/adafruit_ble/characteristics/json.py b/adafruit_ble/characteristics/json.py new file mode 100644 index 0000000..98869bd --- /dev/null +++ b/adafruit_ble/characteristics/json.py @@ -0,0 +1,53 @@ +""" +`json` +==================================================== + +This module provides Json characteristic. + +""" + +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.""" + + 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): + return json.dumps(value).encode("utf-8") + + @staticmethod + def unpack(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)) From 036059891c54e031e40fa7e1b0d8d45a42160ec5 Mon Sep 17 00:00:00 2001 From: mraleson Date: Sat, 16 Apr 2022 14:17:59 -0600 Subject: [PATCH 02/11] Add missing copyright information --- adafruit_ble/characteristics/json.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/adafruit_ble/characteristics/json.py b/adafruit_ble/characteristics/json.py index 98869bd..0ecfc0e 100644 --- a/adafruit_ble/characteristics/json.py +++ b/adafruit_ble/characteristics/json.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2022 Mark Raleson +# +# SPDX-License-Identifier: MIT + """ `json` ==================================================== From 25255d5b56359f6babbef817f1871d929da5f531 Mon Sep 17 00:00:00 2001 From: mraleson Date: Sat, 16 Apr 2022 14:22:59 -0600 Subject: [PATCH 03/11] Add missing JSON doc strings to pass CI linter checks --- adafruit_ble/characteristics/json.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/adafruit_ble/characteristics/json.py b/adafruit_ble/characteristics/json.py index 0ecfc0e..d8c2958 100644 --- a/adafruit_ble/characteristics/json.py +++ b/adafruit_ble/characteristics/json.py @@ -42,10 +42,12 @@ def __init__( @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): From 65f350b7feadef47d619de85859d83451dd68d08 Mon Sep 17 00:00:00 2001 From: mraleson Date: Sat, 16 Apr 2022 14:31:04 -0600 Subject: [PATCH 04/11] Clarify usage and limitations in doc string --- adafruit_ble/characteristics/json.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_ble/characteristics/json.py b/adafruit_ble/characteristics/json.py index d8c2958..38ba657 100644 --- a/adafruit_ble/characteristics/json.py +++ b/adafruit_ble/characteristics/json.py @@ -19,7 +19,7 @@ class JsonCharacteristic(Characteristic): - """Json string characteristic.""" + """Json string characteristic for JSON serializable values of a limited size (max_length).""" def __init__( self, From 5eb2b5b4d6c3c5232b593c9c0a2ff25d91f1c546 Mon Sep 17 00:00:00 2001 From: mraleson Date: Mon, 18 Apr 2022 17:59:31 -0600 Subject: [PATCH 05/11] Add Json Characteristic examples --- examples/ble_json_central.py | 32 +++++++++++++++++++++ examples/ble_json_peripheral.py | 49 +++++++++++++++++++++++++++++++++ examples/ble_json_service.py | 30 ++++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 examples/ble_json_central.py create mode 100644 examples/ble_json_peripheral.py create mode 100644 examples/ble_json_service.py diff --git a/examples/ble_json_central.py b/examples/ble_json_central.py new file mode 100644 index 0000000..3de8870 --- /dev/null +++ b/examples/ble_json_central.py @@ -0,0 +1,32 @@ +# SPDX-FileCopyrightText: 2020 Mark Raleson +# +# SPDX-License-Identifier: MIT + +# Read sensor readings from peripheral BLE device using JSON characteristic. + +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) diff --git a/examples/ble_json_peripheral.py b/examples/ble_json_peripheral.py new file mode 100644 index 0000000..99c44dc --- /dev/null +++ b/examples/ble_json_peripheral.py @@ -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') diff --git a/examples/ble_json_service.py b/examples/ble_json_service.py new file mode 100644 index 0000000..41fc2fa --- /dev/null +++ b/examples/ble_json_service.py @@ -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 From 35308aee84b70f46ca02adb19235f3f30acfb0bd Mon Sep 17 00:00:00 2001 From: mraleson Date: Tue, 19 Apr 2022 14:54:54 -0600 Subject: [PATCH 06/11] Fix naming convention Json to JSON --- adafruit_ble/characteristics/json.py | 6 +++--- examples/ble_json_peripheral.py | 2 +- examples/ble_json_service.py | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/adafruit_ble/characteristics/json.py b/adafruit_ble/characteristics/json.py index 38ba657..8e49951 100644 --- a/adafruit_ble/characteristics/json.py +++ b/adafruit_ble/characteristics/json.py @@ -6,7 +6,7 @@ `json` ==================================================== -This module provides Json characteristic. +This module provides JSON characteristic. """ @@ -18,8 +18,8 @@ __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).""" +class JSONCharacteristic(Characteristic): + """JSON string characteristic for JSON serializable values of a limited size (max_length).""" def __init__( self, diff --git a/examples/ble_json_peripheral.py b/examples/ble_json_peripheral.py index 99c44dc..42611d4 100644 --- a/examples/ble_json_peripheral.py +++ b/examples/ble_json_peripheral.py @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: MIT -# Provide readable sensor values and writable settings to connected devices via Json characteristic. +# Provide readable sensor values and writable settings to connected devices via JSON characteristic. import time import random diff --git a/examples/ble_json_service.py b/examples/ble_json_service.py index 41fc2fa..fb87a68 100644 --- a/examples/ble_json_service.py +++ b/examples/ble_json_service.py @@ -1,18 +1,18 @@ 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 +from adafruit_ble.characteristics.json import JSONCharacteristic -# A custom service with two Json characteristics for this device. The "sensors" characteristic +# 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. +# NOTE: JSON data is limited by characteristic max_length of 512 byes. class SensorService(Service): uuid = VendorUUID("51ad213f-e568-4e35-84e4-67af89c79ef0") - settings = JsonCharacteristic( + settings = JSONCharacteristic( uuid=VendorUUID("e077bdec-f18b-4944-9e9e-8b3a815162b4"), properties=Characteristic.READ | Characteristic.WRITE, initial_value={ @@ -20,7 +20,7 @@ class SensorService(Service): } ) - sensors = JsonCharacteristic( + sensors = JSONCharacteristic( uuid=VendorUUID("528ff74b-fdb8-444c-9c64-3dd5da4135ae"), properties=Characteristic.READ, ) From da662282d8dca8b71b3af2c26b2cae5a7a615b43 Mon Sep 17 00:00:00 2001 From: mraleson Date: Tue, 19 Apr 2022 15:04:52 -0600 Subject: [PATCH 07/11] Fix grammar and expand module docstring --- adafruit_ble/characteristics/json.py | 2 +- examples/ble_json_central.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/adafruit_ble/characteristics/json.py b/adafruit_ble/characteristics/json.py index 8e49951..9ac68b5 100644 --- a/adafruit_ble/characteristics/json.py +++ b/adafruit_ble/characteristics/json.py @@ -6,7 +6,7 @@ `json` ==================================================== -This module provides JSON characteristic. +This module provides a JSON characteristic for reading/writing JSON serializable Python values. """ diff --git a/examples/ble_json_central.py b/examples/ble_json_central.py index 3de8870..e37a3f2 100644 --- a/examples/ble_json_central.py +++ b/examples/ble_json_central.py @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: MIT -# Read sensor readings from peripheral BLE device using JSON characteristic. +# Read sensor readings from peripheral BLE device using a JSON characteristic. from adafruit_ble import BLERadio from adafruit_ble.advertising.standard import ProvideServicesAdvertisement From 1bb93f910d2b66d86d168faf86da4cb4af97aac7 Mon Sep 17 00:00:00 2001 From: mraleson Date: Tue, 19 Apr 2022 15:10:21 -0600 Subject: [PATCH 08/11] Resolve linter third party import issue --- examples/ble_json_central.py | 2 +- examples/ble_json_peripheral.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/ble_json_central.py b/examples/ble_json_central.py index e37a3f2..ff2470f 100644 --- a/examples/ble_json_central.py +++ b/examples/ble_json_central.py @@ -4,9 +4,9 @@ # 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 -from ble_json_service import SensorService ble = BLERadio() diff --git a/examples/ble_json_peripheral.py b/examples/ble_json_peripheral.py index 42611d4..87e871a 100644 --- a/examples/ble_json_peripheral.py +++ b/examples/ble_json_peripheral.py @@ -6,9 +6,9 @@ import time import random +from ble_json_service import SensorService 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. From b12e8aef6a268a6030879a00e909d539a2093ecf Mon Sep 17 00:00:00 2001 From: mraleson Date: Fri, 22 Apr 2022 18:35:22 -0600 Subject: [PATCH 09/11] Fix linter warning by disabling two-few-public-methods --- examples/ble_json_service.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/ble_json_service.py b/examples/ble_json_service.py index fb87a68..acdc71a 100644 --- a/examples/ble_json_service.py +++ b/examples/ble_json_service.py @@ -10,6 +10,8 @@ # 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( From 2497a620d3c054f01dadf90dfd620266f3572721 Mon Sep 17 00:00:00 2001 From: mraleson Date: Fri, 22 Apr 2022 20:10:14 -0600 Subject: [PATCH 10/11] Fix missing copyright on example json service --- examples/ble_json_service.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/ble_json_service.py b/examples/ble_json_service.py index acdc71a..9a5b963 100644 --- a/examples/ble_json_service.py +++ b/examples/ble_json_service.py @@ -1,3 +1,9 @@ +# 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 7d155748d96743e7b40c97638fcf0009827f5565 Mon Sep 17 00:00:00 2001 From: mraleson Date: Fri, 22 Apr 2022 20:23:56 -0600 Subject: [PATCH 11/11] Fix formatting using black --- examples/ble_json_central.py | 6 ++---- examples/ble_json_peripheral.py | 22 ++++++++++------------ examples/ble_json_service.py | 4 +--- 3 files changed, 13 insertions(+), 19 deletions(-) diff --git a/examples/ble_json_central.py b/examples/ble_json_central.py index ff2470f..51a04e8 100644 --- a/examples/ble_json_central.py +++ b/examples/ble_json_central.py @@ -25,8 +25,6 @@ if connection and connection.connected: service = connection[SensorService] - service.settings = { - 'unit': 'celsius' # 'fahrenheit' - } + service.settings = {"unit": "celsius"} # 'fahrenheit' while connection.connected: - print('Sensors: ', service.sensors) + print("Sensors: ", service.sensors) diff --git a/examples/ble_json_peripheral.py b/examples/ble_json_peripheral.py index 87e871a..67337cf 100644 --- a/examples/ble_json_peripheral.py +++ b/examples/ble_json_peripheral.py @@ -20,17 +20,15 @@ def measure(unit): temperature = random.uniform(0.0, 10.0) humidity = random.uniform(0.0, 100.0) - if unit == 'fahrenheit': + if unit == "fahrenheit": temperature = (temperature * 9.0 / 5.0) + 32.0 - return { - 'temperature': temperature, - 'humidity': humidity - } + 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 + 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...") @@ -40,10 +38,10 @@ def measure(unit): print("Connected") while ble.connected: settings = service.settings - measurement = measure(settings.get('unit', 'celsius')) + measurement = measure(settings.get("unit", "celsius")) service.sensors = measurement - print('Settings: ', settings) - print('Sensors: ', measurement) - time.sleep(.25) + print("Settings: ", settings) + print("Sensors: ", measurement) + time.sleep(0.25) - print('Disconnected') + print("Disconnected") diff --git a/examples/ble_json_service.py b/examples/ble_json_service.py index 9a5b963..6351564 100644 --- a/examples/ble_json_service.py +++ b/examples/ble_json_service.py @@ -23,9 +23,7 @@ class SensorService(Service): settings = JSONCharacteristic( uuid=VendorUUID("e077bdec-f18b-4944-9e9e-8b3a815162b4"), properties=Characteristic.READ | Characteristic.WRITE, - initial_value={ - 'unit': 'celsius' - } + initial_value={"unit": "celsius"}, ) sensors = JSONCharacteristic(