Skip to content

change to ruff #33

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 1 commit into from
May 15, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# SPDX-FileCopyrightText: 2024 Justin Myers for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense

.py text eol=lf
.rst text eol=lf
.txt text eol=lf
.yaml text eol=lf
.toml text eol=lf
.license text eol=lf
.md text eol=lf
43 changes: 11 additions & 32 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,42 +1,21 @@
# SPDX-FileCopyrightText: 2020 Diego Elio Pettenò
# SPDX-FileCopyrightText: 2024 Justin Myers for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense

repos:
- repo: https://github.com/python/black
rev: 23.3.0
hooks:
- id: black
- repo: https://github.com/fsfe/reuse-tool
rev: v1.1.2
hooks:
- id: reuse
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v4.5.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/pycqa/pylint
rev: v2.17.4
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.3.4
hooks:
- id: pylint
name: pylint (library code)
types: [python]
args:
- --disable=consider-using-f-string
exclude: "^(docs/|examples/|tests/|setup.py$)"
- id: pylint
name: pylint (example code)
description: Run pylint rules on "examples/*.py" files
types: [python]
files: "^examples/"
args:
- --disable=missing-docstring,invalid-name,consider-using-f-string,duplicate-code
- id: pylint
name: pylint (test code)
description: Run pylint rules on "tests/*.py" files
types: [python]
files: "^tests/"
args:
- --disable=missing-docstring,consider-using-f-string,duplicate-code
- id: ruff-format
- id: ruff
args: ["--fix"]
- repo: https://github.com/fsfe/reuse-tool
rev: v3.0.1
hooks:
- id: reuse
399 changes: 0 additions & 399 deletions .pylintrc

This file was deleted.

6 changes: 3 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
@@ -13,9 +13,9 @@ Introduction
:target: https://github.com/adafruit/Adafruit_CircuitPython_BLE_BroadcastNet/actions
:alt: Build Status

.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/psf/black
:alt: Code Style: Black
.. image:: https://img.shields.io/endpoint?url=https://github.com/raw/astral-sh/ruff/main/assets/badge/v2.json
:target: https://github.com/astral-sh/ruff
:alt: Code Style: Ruff

Basic IOT over BLE advertisements.

37 changes: 15 additions & 22 deletions adafruit_ble_broadcastnet.py
Original file line number Diff line number Diff line change
@@ -12,41 +12,40 @@
* Author(s): Scott Shawcroft
"""

import struct
import os
import struct
import time

import adafruit_ble
from adafruit_ble.advertising import Advertisement, LazyObjectField
from adafruit_ble.advertising.standard import ManufacturerData, ManufacturerDataField
from adafruit_ble.advertising.adafruit import (
MANUFACTURING_DATA_ADT,
ADAFRUIT_COMPANY_ID,
MANUFACTURING_DATA_ADT,
)
from adafruit_ble.advertising.standard import ManufacturerData, ManufacturerDataField

try:
from typing import Optional

from _bleio import ScanEntry
except ImportError:
pass

__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BLE_BroadcastNet.git"

_ble = adafruit_ble.BLERadio() # pylint: disable=invalid-name
_sequence_number = 0 # pylint: disable=invalid-name
_ble = adafruit_ble.BLERadio()
_sequence_number = 0


def broadcast(
measurement: "AdafruitSensorMeasurement",
*,
broadcast_time: float = 0.1,
extended: bool = False
measurement: "AdafruitSensorMeasurement", *, broadcast_time: float = 0.1, extended: bool = False
) -> None:
"""Broadcasts the given measurement for the given broadcast time. If extended is False and the
measurement would be too long, it will be split into multiple measurements for transmission,
each with the given broadcast time.
"""
global _sequence_number # pylint: disable=global-statement,invalid-name
global _sequence_number # noqa: PLW0603
for submeasurement in measurement.split(252 if extended else 31):
submeasurement.sequence_number = _sequence_number
_ble.start_advertising(submeasurement, scan_response=None)
@@ -59,16 +58,12 @@ def broadcast(
if not hasattr(os, "environ") or (
"GITHUB_ACTION" not in os.environ and "READTHEDOCS" not in os.environ
):
if _ble._adapter.address: # pylint: disable=protected-access
device_address = "{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}".format( # pylint: disable=invalid-name
*reversed(
list(
_ble._adapter.address.address_bytes # pylint: disable=protected-access
)
)
if _ble._adapter.address:
device_address = "{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}".format(
*reversed(list(_ble._adapter.address.address_bytes))
)
else:
device_address = "000000000000" # pylint: disable=invalid-name
device_address = "000000000000"
"""Device address as a string."""


@@ -162,9 +157,7 @@ class AdafruitSensorMeasurement(Advertisement):
sound_level = ManufacturerDataField(0x0A16, "<f")
"Sound level as a float"

def __init__(
self, *, entry: Optional[ScanEntry] = None, sequence_number: int = 0
) -> None:
def __init__(self, *, entry: Optional[ScanEntry] = None, sequence_number: int = 0) -> None:
super().__init__(entry=entry)
if entry:
return
@@ -177,7 +170,7 @@ def __str__(self) -> str:
if issubclass(attribute_instance.__class__, ManufacturerDataField):
value = getattr(self, attr)
if value is not None:
parts.append("{}={}".format(attr, str(value)))
parts.append(f"{attr}={str(value)}")
return "<{} {} >".format(self.__class__.__name__, " ".join(parts))

def split(self, max_packet_size: int = 31) -> "AdafruitSensorMeasurement":
3 changes: 3 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
@@ -4,5 +4,8 @@
.. If your library file(s) are nested in a directory (e.g. /adafruit_foo/foo.py)
.. use this format as the module name: "adafruit_foo.foo"
API Reference
#############

.. automodule:: adafruit_ble_broadcastnet
:members:
8 changes: 2 additions & 6 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
# -*- coding: utf-8 -*-

# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
#
# SPDX-License-Identifier: MIT

import datetime
import os
import sys
import datetime

sys.path.insert(0, os.path.abspath(".."))

@@ -48,9 +46,7 @@
creation_year = "2020"
current_year = str(datetime.datetime.now().year)
year_duration = (
current_year
if current_year == creation_year
else creation_year + " - " + current_year
current_year if current_year == creation_year else creation_year + " - " + current_year
)
copyright = year_duration + " Scott Shawcroft"
author = "Scott Shawcroft"
13 changes: 4 additions & 9 deletions examples/ble_broadcastnet_battery_level.py
Original file line number Diff line number Diff line change
@@ -2,9 +2,11 @@
# SPDX-License-Identifier: MIT

import time

import analogio
import board
import microcontroller

import adafruit_ble_broadcastnet

print("This is BroadcastNet sensor:", adafruit_ble_broadcastnet.device_address)
@@ -14,16 +16,9 @@

while True:
measurement = adafruit_ble_broadcastnet.AdafruitSensorMeasurement()
battery_voltage = (
battery.value
/ 2**16
* divider_ratio
* battery.reference_voltage # pylint: disable=no-member
)
battery_voltage = battery.value / 2**16 * divider_ratio * battery.reference_voltage
measurement.battery_voltage = int(battery_voltage * 1000)
measurement.temperature = (
microcontroller.cpu.temperature # pylint: disable=no-member
)
measurement.temperature = microcontroller.cpu.temperature
print(measurement)
adafruit_ble_broadcastnet.broadcast(measurement)

13 changes: 4 additions & 9 deletions examples/ble_broadcastnet_battery_level_neopixel.py
Original file line number Diff line number Diff line change
@@ -2,10 +2,12 @@
# SPDX-License-Identifier: MIT

import time

import analogio
import board
import microcontroller
import neopixel

import adafruit_ble_broadcastnet

print("This is BroadcastNet sensor:", adafruit_ble_broadcastnet.device_address)
@@ -17,21 +19,14 @@

while True:
measurement = adafruit_ble_broadcastnet.AdafruitSensorMeasurement()
battery_voltage = (
battery.value
/ 2**16
* divider_ratio
* battery.reference_voltage # pylint: disable=no-member
)
battery_voltage = battery.value / 2**16 * divider_ratio * battery.reference_voltage
r = 16 - int(((battery_voltage - 3.6) / 0.6) * 16)
r = min(16, max(r, 0))
g = int(((battery_voltage - 3.6) / 0.6) * 16)
g = min(16, max(g, 0))
pixel[0] = r << 16 | g << 8
measurement.battery_voltage = int(battery_voltage * 1000)
measurement.temperature = (
microcontroller.cpu.temperature # pylint: disable=no-member
)
measurement.temperature = microcontroller.cpu.temperature
print(measurement)
adafruit_ble_broadcastnet.broadcast(measurement)
pixel[0] = 0
27 changes: 11 additions & 16 deletions examples/ble_broadcastnet_blinka_bridge.py
Original file line number Diff line number Diff line change
@@ -2,12 +2,15 @@
# SPDX-License-Identifier: MIT

"""This example bridges from BLE to Adafruit IO on a Raspberry Pi."""
from os import getenv

import time
from os import getenv

import adafruit_ble
import requests
from adafruit_ble.advertising.standard import ManufacturerDataField
from adafruit_blinka import load_settings_toml
import adafruit_ble

import adafruit_ble_broadcastnet

# Get Adafruit IO keys, ensure these are setup in settings.toml
@@ -30,10 +33,6 @@ def aio_get(path, **kwargs):
return requests.get(aio_base_url + path, **kwargs)


# Disable outer names check because we frequently collide.
# pylint: disable=redefined-outer-name


def create_group(name):
response = aio_post("/groups", json={"name": name})
if response.status_code != 201:
@@ -45,9 +44,7 @@ def create_group(name):


def create_feed(group_key, name):
response = aio_post(
"/groups/{}/feeds".format(group_key), json={"feed": {"name": name}}
)
response = aio_post(f"/groups/{group_key}/feeds", json={"feed": {"name": name}})
if response.status_code != 201:
print(name)
print(response.content)
@@ -57,7 +54,7 @@ def create_feed(group_key, name):


def create_data(group_key, data):
response = aio_post("/groups/{}/data".format(group_key), json={"feeds": data})
response = aio_post(f"/groups/{group_key}/data", json={"feeds": data})
if response.status_code == 429:
print("Throttled!")
return False
@@ -132,9 +129,9 @@ def convert_to_feed_data(values, attribute_name, attribute_instance):
number_missed = measurement.sequence_number - sequence_numbers[sensor_address] - 1
if number_missed < 0:
number_missed += 256
group_key = "bridge-{}-sensor-{}".format(bridge_address, sensor_address)
group_key = f"bridge-{bridge_address}-sensor-{sensor_address}"
if sensor_address not in existing_feeds:
create_group("Bridge {} Sensor {}".format(bridge_address, sensor_address))
create_group(f"Bridge {bridge_address} Sensor {sensor_address}")
create_feed(group_key, "Missed Message Count")
existing_feeds[sensor_address] = ["missed-message-count"]

@@ -145,9 +142,7 @@ def convert_to_feed_data(values, attribute_name, attribute_instance):
if attribute != "sequence_number":
values = getattr(measurement, attribute)
if values is not None:
data.extend(
convert_to_feed_data(values, attribute, attribute_instance)
)
data.extend(convert_to_feed_data(values, attribute, attribute_instance))

for feed_data in data:
if feed_data["key"] not in existing_feeds[sensor_address]:
@@ -161,7 +156,7 @@ def convert_to_feed_data(values, attribute_name, attribute_instance):
sequence_numbers[sensor_address] = measurement.sequence_number

duration = time.monotonic() - start_time
print("Done logging measurement to IO. Took {} seconds".format(duration))
print(f"Done logging measurement to IO. Took {duration} seconds")
print()

print("scan done")
31 changes: 13 additions & 18 deletions examples/ble_broadcastnet_bridge.py
Original file line number Diff line number Diff line change
@@ -2,15 +2,18 @@
# SPDX-License-Identifier: MIT

"""This example bridges from BLE to Adafruit IO on a CircuitPython device that
supports both WiFi and BLE. (The first chip is the ESP32-S3.)"""
from os import getenv
supports both WiFi and BLE. (The first chip is the ESP32-S3.)"""

import time
from os import getenv

import adafruit_ble
import adafruit_connection_manager
import adafruit_requests as requests
from adafruit_ble.advertising.standard import ManufacturerDataField
import adafruit_ble
import board
import wifi
from adafruit_ble.advertising.standard import ManufacturerDataField

import adafruit_ble_broadcastnet

# To get a status neopixel flashing, install the neopixel library as well.
@@ -57,10 +60,6 @@ def aio_get(path, **kwargs):
return requests.get(aio_base_url + path, **kwargs)


# Disable outer names check because we frequently collide.
# pylint: disable=redefined-outer-name


def create_group(name):
response = aio_post("/groups", json={"name": name})
if response.status_code != 201:
@@ -72,9 +71,7 @@ def create_group(name):


def create_feed(group_key, name):
response = aio_post(
"/groups/{}/feeds".format(group_key), json={"feed": {"name": name}}
)
response = aio_post(f"/groups/{group_key}/feeds", json={"feed": {"name": name}})
if response.status_code != 201:
print(name)
print(response.content)
@@ -84,7 +81,7 @@ def create_feed(group_key, name):


def create_data(group_key, data):
response = aio_post("/groups/{}/data".format(group_key), json={"feeds": data})
response = aio_post(f"/groups/{group_key}/data", json={"feeds": data})
if response.status_code == 429:
print("Throttled!")
return False
@@ -162,9 +159,9 @@ def convert_to_feed_data(values, attribute_name, attribute_instance):
# Derive the status color from the sensor address.
if status_pixel:
status_pixel[0] = rainbowio.colorwheel(sum(reversed_address))
group_key = "bridge-{}-sensor-{}".format(bridge_address, sensor_address)
group_key = f"bridge-{bridge_address}-sensor-{sensor_address}"
if sensor_address not in existing_feeds:
create_group("Bridge {} Sensor {}".format(bridge_address, sensor_address))
create_group(f"Bridge {bridge_address} Sensor {sensor_address}")
create_feed(group_key, "Missed Message Count")
existing_feeds[sensor_address] = ["missed-message-count"]

@@ -175,9 +172,7 @@ def convert_to_feed_data(values, attribute_name, attribute_instance):
if attribute != "sequence_number":
values = getattr(measurement, attribute)
if values is not None:
data.extend(
convert_to_feed_data(values, attribute, attribute_instance)
)
data.extend(convert_to_feed_data(values, attribute, attribute_instance))

for feed_data in data:
if feed_data["key"] not in existing_feeds[sensor_address]:
@@ -193,7 +188,7 @@ def convert_to_feed_data(values, attribute_name, attribute_instance):
duration = time.monotonic() - start_time
if status_pixel:
status_pixel[0] = 0x000000
print("Done logging measurement to IO. Took {} seconds".format(duration))
print(f"Done logging measurement to IO. Took {duration} seconds")
print()

print("scan done")
2 changes: 2 additions & 0 deletions examples/ble_broadcastnet_cpb.py
Original file line number Diff line number Diff line change
@@ -4,7 +4,9 @@
"""This uses the CircuitPlayground Bluefruit as a sensor node."""

import time

from adafruit_circuitplayground import cp

import adafruit_ble_broadcastnet

print("This is BroadcastNet sensor:", adafruit_ble_broadcastnet.device_address)
17 changes: 7 additions & 10 deletions examples/ble_broadcastnet_expo_backoff.py
Original file line number Diff line number Diff line change
@@ -2,15 +2,17 @@
# SPDX-License-Identifier: MIT

"""This example uses the internal temperature sensor and reports the battery voltage. However, it
reads the temperature more often but only reports it when it's changed by a degree since the last
report. When doing a report it will actually do multiple broadcasts and wait 2 ** n readings
until the next broadcast. The delay is reset every time the temp moves more than 1 degree."""
reads the temperature more often but only reports it when it's changed by a degree since the last
report. When doing a report it will actually do multiple broadcasts and wait 2 ** n readings
until the next broadcast. The delay is reset every time the temp moves more than 1 degree."""

import math
import time

import analogio
import board
import microcontroller

import adafruit_ble_broadcastnet

print("This is BroadcastNet sensor:", adafruit_ble_broadcastnet.device_address)
@@ -21,7 +23,7 @@
last_temperature = None
consecutive = 1
while True:
temp = microcontroller.cpu.temperature # pylint: disable=no-member
temp = microcontroller.cpu.temperature
if not last_temperature or abs(temp - last_temperature) > 1:
consecutive = 1
last_temperature = temp
@@ -33,12 +35,7 @@
exp = int(math.log(consecutive, 2))
if 2**exp == consecutive:
measurement = adafruit_ble_broadcastnet.AdafruitSensorMeasurement()
battery_voltage = (
battery.value
/ 2**16
* divider_ratio
* battery.reference_voltage # pylint: disable=no-member
)
battery_voltage = battery.value / 2**16 * divider_ratio * battery.reference_voltage
measurement.battery_voltage = int(battery_voltage * 1000)
measurement.temperature = temp
print(measurement)
6 changes: 4 additions & 2 deletions examples/ble_broadcastnet_multisensor.py
Original file line number Diff line number Diff line change
@@ -4,13 +4,15 @@
"""This is a complex sensor node that uses the sensors on a Clue and Feather Bluefruit Sense."""

import time
import board

import adafruit_bmp280
import adafruit_sht31d

# import adafruit_apds9960.apds9960
import adafruit_lis3mdl
import adafruit_lsm6ds
import adafruit_sht31d
import board

import adafruit_ble_broadcastnet

print("This is BroadcastNet sensor:", adafruit_ble_broadcastnet.device_address)
5 changes: 2 additions & 3 deletions examples/ble_broadcastnet_scan_test.py
Original file line number Diff line number Diff line change
@@ -4,13 +4,12 @@
"""This example merely scans for broadcastnet packets to check that something is sending them."""

import adafruit_ble

import adafruit_ble_broadcastnet

ble = adafruit_ble.BLERadio()

print("scanning")
# By providing Advertisement as well we include everything, not just specific advertisements.
for advert in ble.start_scan(
adafruit_ble_broadcastnet.AdafruitSensorMeasurement, interval=0.5
):
for advert in ble.start_scan(adafruit_ble_broadcastnet.AdafruitSensorMeasurement, interval=0.5):
print(advert)
8 changes: 4 additions & 4 deletions examples/ble_broadcastnet_simpletest.py
Original file line number Diff line number Diff line change
@@ -2,19 +2,19 @@
# SPDX-License-Identifier: MIT

"""This is a basic sensor node that uses the internal temperature sensor and reports it every 10
seconds."""
seconds."""

import time

import microcontroller

import adafruit_ble_broadcastnet

print("This is BroadcastNet sensor:", adafruit_ble_broadcastnet.device_address)

while True:
measurement = adafruit_ble_broadcastnet.AdafruitSensorMeasurement()
measurement.temperature = (
microcontroller.cpu.temperature # pylint: disable=no-member
)
measurement.temperature = microcontroller.cpu.temperature
print(measurement)
adafruit_ble_broadcastnet.broadcast(measurement)
time.sleep(10)
107 changes: 107 additions & 0 deletions ruff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT

target-version = "py38"
line-length = 100

[lint]
preview = true
select = ["I", "PL", "UP"]

extend-select = [
"D419", # empty-docstring
"E501", # line-too-long
"W291", # trailing-whitespace
"PLC0414", # useless-import-alias
"PLC2401", # non-ascii-name
"PLC2801", # unnecessary-dunder-call
"PLC3002", # unnecessary-direct-lambda-call
"E999", # syntax-error
"PLE0101", # return-in-init
"F706", # return-outside-function
"F704", # yield-outside-function
"PLE0116", # continue-in-finally
"PLE0117", # nonlocal-without-binding
"PLE0241", # duplicate-bases
"PLE0302", # unexpected-special-method-signature
"PLE0604", # invalid-all-object
"PLE0605", # invalid-all-format
"PLE0643", # potential-index-error
"PLE0704", # misplaced-bare-raise
"PLE1141", # dict-iter-missing-items
"PLE1142", # await-outside-async
"PLE1205", # logging-too-many-args
"PLE1206", # logging-too-few-args
"PLE1307", # bad-string-format-type
"PLE1310", # bad-str-strip-call
"PLE1507", # invalid-envvar-value
"PLE2502", # bidirectional-unicode
"PLE2510", # invalid-character-backspace
"PLE2512", # invalid-character-sub
"PLE2513", # invalid-character-esc
"PLE2514", # invalid-character-nul
"PLE2515", # invalid-character-zero-width-space
"PLR0124", # comparison-with-itself
"PLR0202", # no-classmethod-decorator
"PLR0203", # no-staticmethod-decorator
"UP004", # useless-object-inheritance
"PLR0206", # property-with-parameters
"PLR0904", # too-many-public-methods
"PLR0911", # too-many-return-statements
"PLR0912", # too-many-branches
"PLR0913", # too-many-arguments
"PLR0914", # too-many-locals
"PLR0915", # too-many-statements
"PLR0916", # too-many-boolean-expressions
"PLR1702", # too-many-nested-blocks
"PLR1704", # redefined-argument-from-local
"PLR1711", # useless-return
"C416", # unnecessary-comprehension
"PLR1733", # unnecessary-dict-index-lookup
"PLR1736", # unnecessary-list-index-lookup

# ruff reports this rule is unstable
#"PLR6301", # no-self-use

"PLW0108", # unnecessary-lambda
"PLW0120", # useless-else-on-loop
"PLW0127", # self-assigning-variable
"PLW0129", # assert-on-string-literal
"B033", # duplicate-value
"PLW0131", # named-expr-without-context
"PLW0245", # super-without-brackets
"PLW0406", # import-self
"PLW0602", # global-variable-not-assigned
"PLW0603", # global-statement
"PLW0604", # global-at-module-level

# fails on the try: import typing used by libraries
#"F401", # unused-import

"F841", # unused-variable
"E722", # bare-except
"PLW0711", # binary-op-exception
"PLW1501", # bad-open-mode
"PLW1508", # invalid-envvar-default
"PLW1509", # subprocess-popen-preexec-fn
"PLW2101", # useless-with-lock
"PLW3301", # nested-min-max
]

ignore = [
"PLR2004", # magic-value-comparison
"UP030", # format literals
"PLW1514", # unspecified-encoding
"PLR0913", # too-many-arguments
"PLR0915", # too-many-statements
"PLR0917", # too-many-positional-arguments
"PLR0904", # too-many-public-methods
"PLR0912", # too-many-branches
"PLR0916", # too-many-boolean-expressions
"PLR6301", # could-be-static no-self-use
"PLC0415", # import outside toplevel
]

[format]
line-ending = "lf"