Skip to main content
Version: 1.0

Bluetooth

Since version 0.12.0

The Bluetooth Low Energy (BLE) API enables scripts to scan for and interact with Bluetooth devices, including beacons, sensors, and other BLE peripherals.

Overview

The BLE global object provides:

  • Scanner - Discover and monitor Bluetooth devices
  • GAP - Parse advertisement data using Generic Access Profile utilities
  • AdvBuilder - Advertising support
Device Support

Bluetooth functionality requires specific hardware support. Not all Shelly devices have Bluetooth capabilities. Check your device specifications.

Scan Manager Behavior

Since firmware 1.5.0, the Enhanced Scan Manager handles all Bluetooth scanning. Multiple scripts can request scans simultaneously, and the manager will merge scan parameters using the most aggressive settings:

  • Shortest duration_ms that satisfies all requests
  • active scan if any request requires it
  • Shortest interval_ms among all requests
  • Longest window_ms among all requests (max 100ms)
  • Lowest rssi_thr among all requests
  • Each script's filters are applied independently

This means your actual scan parameters may differ from what you requested if other scripts or system components are also scanning.

BLE.Scanner

The Scanner object manages Bluetooth device discovery.

Constants

Scan Events:

  • BLE.Scanner.SCAN_START = 0
  • BLE.Scanner.SCAN_STOP = 1
  • BLE.Scanner.SCAN_RESULT = 2

Duration:

  • BLE.Scanner.INFINITE_SCAN = -1 (perpetual scanning)

BLE.Scanner.subscribe()

Subscribe to scan events.

Syntax:

BLE.Scanner.subscribe(callback[, userdata]) -> undefined
PropertyTypeDescription

callback

function or null

Function for scan events (null to unsubscribe)

PropertyTypeDescription

event

number

Event type (SCAN_START, SCAN_STOP, or SCAN_RESULT)

result

object

Scan result for SCAN_RESULT events

PropertyTypeDescription

addr

string

Device MAC address

addr_type

number

Address type

advData

string

Advertisement data

scanRsp

string

Scan response (active scans)

rssi

number

Signal strength

flags

number

Advertisement flags

local_name

string

Device name

manufacturer_data

object

Manufacturer-specific data

service_uuids

array

Advertised service UUIDs

service_data

object

Service-specific data

tx_power_level

number

Transmit power

userdata

any

Passed userdata

userdata

any

Data to pass to callback

Example:

BLE.Scanner.subscribe(function(event, result) {
switch(event) {
case BLE.Scanner.SCAN_START:
console.log("Scan started");
break;
case BLE.Scanner.SCAN_STOP:
console.log("Scan stopped");
break;
case BLE.Scanner.SCAN_RESULT:
console.log("Found device:", result.addr, "RSSI:", result.rssi);
if (result.local_name) {
console.log("Name:", result.local_name);
}
break;
}
});

BLE.Scanner.start()

Start scanning for Bluetooth devices with optional filters and configuration.

Syntax:

BLE.Scanner.start(options[, callback[, userdata]]) -> object or null
PropertyTypeDescription

options

object

Scan configuration object

callback

function

Optional event callback function

userdata

any

Data passed to callback

Returns: Scan options object on success, or null if scan failed to start

Scan Options and Filters

For complete documentation of scan options and filters, see:

  • Scan Options - Configure duration, interval, window, RSSI threshold
  • Scan Filters - Filter by MAC address, name, services, manufacturer data (firmware 2.0.0+)

Basic Example

// Start 30-second scan with RSSI filtering
BLE.Scanner.start({
duration_ms: 30000,
active: true,
interval_ms: 100,
window_ms: 50,
rssi_thr: -70 // Only devices with RSSI > -70 dBm
}, function(event, result) {
if (event === BLE.Scanner.SCAN_RESULT) {
console.log("Device:", result.addr, "RSSI:", result.rssi);
}
});
PropertyTypeDescription

options

object

Scan configuration

PropertyTypeDescription

duration_ms

number

Scan duration (use INFINITE_SCAN for continuous)

active

boolean

Active scan (true) or passive (false)

interval_ms

number

Scan interval in ms

window_ms

number

Scan window in ms (max 100ms)

rssi_thr

number

RSSI threshold filter

filters

array

Device filters

callback

function

Optional event callback

userdata

any

Data for callback

Returns: Scan options object or null if failed

Example:

// Start 30-second scan
BLE.Scanner.start({
duration_ms: 30000,
active: true,
interval_ms: 100,
window_ms: 50
}, function(event, result) {
if (event === BLE.Scanner.SCAN_RESULT) {
console.log("Device:", result.addr);
}
});

// Continuous scan with RSSI filter
BLE.Scanner.start({
duration_ms: BLE.Scanner.INFINITE_SCAN,
rssi_thr: -70 // Only devices with RSSI > -70 dBm
});

BLE.Scanner.stop()

Stop the current scan.

Syntax:

BLE.Scanner.stop() -> boolean

Returns: true if stopped successfully, false on error

BLE.Scanner.isRunning()

Check if a scan is active.

Syntax:

BLE.Scanner.isRunning() -> boolean

Returns: true if scanning, false otherwise

BLE.Scanner.getScanOptions()

Get current scan configuration.

Syntax:

BLE.Scanner.getScanOptions() -> object or null

Returns: Options object or null if not scanning

BLE.GAP

Generic Access Profile utilities for parsing advertisement data.

Constants

Address Types:

  • BLE.GAP.ADDRESS_TYPE_PUBLIC = 0x1
  • BLE.GAP.ADDRESS_TYPE_RANDOM_STATIC = 0x2
  • BLE.GAP.ADDRESS_TYPE_RANDOM_NON_RESOLVABLE = 0x3
  • BLE.GAP.ADDRESS_TYPE_RANDOM_RESOLVABLE = 0x4

EIR Types:

  • BLE.GAP.EIR_FLAGS = 0x1
  • BLE.GAP.EIR_SERVICE_16 = 0x3
  • BLE.GAP.EIR_SERVICE_128 = 0x7
  • BLE.GAP.EIR_SHORT_NAME = 0x8
  • BLE.GAP.EIR_FULL_NAME = 0x9
  • BLE.GAP.EIR_TX_POWER_LEVEL = 0xA
  • BLE.GAP.EIR_SERVICE_DATA_16 = 0x16
  • BLE.GAP.EIR_MANUFACTURER_SPECIFIC_DATA = 0xFF

BLE.GAP.parseName()

Extract device name from advertisement data.

Syntax:

BLE.GAP.parseName(data) -> string or null
PropertyTypeDescription

data

string

Advertisement data to parse

Returns: Device name string, or null if not found

BLE.GAP.parseManufacturerData()

Extract manufacturer-specific data from advertisement data.

Syntax:

BLE.GAP.parseManufacturerData(data) -> object or null
PropertyTypeDescription

data

string

Advertisement data to parse

Returns: Object containing manufacturer data, or null if not found

BLE.GAP.parseServiceData()

Extract data for a specific service UUID from advertisement data.

Syntax:

BLE.GAP.parseServiceData(data, uuid) -> string or null
PropertyTypeDescription

data

string

Advertisement data to parse

uuid

string

Service UUID to look for

Returns: Service data string, or null if not found

BLE.GAP.hasService()

Check if a service UUID is advertised in the advertisement data.

Syntax:

BLE.GAP.hasService(data, uuid) -> boolean
PropertyTypeDescription

data

string

Advertisement data to check

uuid

string

Service UUID to look for

Returns: true if the service UUID is present, false otherwise

Advertising support

Since version 2.0.0

Scripts can broadcast custom BLE advertisements, allowing Shelly devices to act as BLE beacons or peripheral devices. This enables use cases such as:

  • Broadcasting sensor data to nearby BLE scanners
  • Implementing custom BLE beacon protocols
  • Advertising BTHome data for integration with home automation systems
  • Creating location-aware applications using BLE proximity
note

Bluetooth advertising from scripts is available on:

  • All Gen3 and Gen4 mains-powered devices that support scripting
  • Select Gen2 mains-powered devices that support scripting
  • Not supported on Gen 4 devices in Zigbee mode.
  • Not supported on Battery-operated devices (e.g., Shelly Flood Gen4, Shelly Smoke Alarm).
note

Advertisement data must comply with BLE specification limits (maximum 31 bytes for advertisement data and 31 bytes for scan response data).

BLE.AdvBuilder

The BLE.AdvBuilder object provides methods for constructing BLE advertisement data and scan response payloads. It maintains internal state and allows building payloads incrementally by adding various data types.

note

The BLE.AdvBuilder is a singleton object within each script. Always call reset() before building a new payload to clear previous state.

BLE.AdvBuilder.addName()

Adds the device name to the advertisement data.

Syntax:

BLE.AdvBuilder.addName(name[, full]) -> boolean
PropertyTypeDescription

name

string

The device name to advertise

full

boolean

If true, advertise as full name; if false, advertise as short name. Defaults to true. Optional

Returns: true if the name was added successfully, false if adding the name would exceed the maximum advertisement data size.

BLE.AdvBuilder.addShellyManufacturerData()

Adds Shelly-specific manufacturer data to the advertisement. Only the Allterco manufacturer ID (0x0BA9) is allowed.

Syntax:

BLE.AdvBuilder.addShellyManufacturerData(data) -> boolean
PropertyTypeDescription

data

string

Manufacturer-specific data payload (without the manufacturer ID prefix)

Returns: true if the manufacturer data was added successfully, false if adding the data would exceed the maximum advertisement data size.

note

The Allterco manufacturer ID (0x0BA9) is automatically prepended to the data. Only provide the manufacturer-specific payload.

BLE.AdvBuilder.addServiceData()

Adds service data for a specific service UUID to the advertisement.

Syntax:

BLE.AdvBuilder.addServiceData(uuid, data) -> boolean
PropertyTypeDescription

uuid

string

Service UUID (16-bit, 32-bit, or 128-bit format)

data

string

Service-specific data payload

Returns: true if the service data was added successfully, false if adding the data would exceed the maximum advertisement data size.

BLE.AdvBuilder.addBTHomeServiceData()

Adds BTHome service data to the advertisement. This is a convenience method that automatically uses the BTHome service UUID (0xFCD2).

Syntax:

BLE.AdvBuilder.addBTHomeServiceData(data) -> boolean
PropertyTypeDescription

data

string

BTHome payload (should follow BTHome format, can be built with BTHome.DataBuilder)

Returns: true if the BTHome service data was added successfully, false if adding the data would exceed the maximum advertisement data size.

info

This method is designed to work seamlessly with BTHome.DataBuilder. Build your BTHome payload first, then add it to the advertisement.

BLE.AdvBuilder.build()

Builds and returns the complete advertisement data as a string.

Syntax:

BLE.AdvBuilder.build() -> string

Returns: The constructed advertisement data as a string, ready to be used with BLE.advertiseOnce().

BLE.AdvBuilder.reset()

Resets the builder state, clearing all added data.

Syntax:

BLE.AdvBuilder.reset() -> undefined
note

Always call reset() before building a new advertisement payload to ensure a clean state.

BLE.advertiseOnce()

Triggers a 3-second broadcast of the provided advertisement data, immediately preempting any currently active advertisement.

Syntax:

BLE.advertiseOnce(adv_data[, scan_rsp]) -> boolean
PropertyTypeDescription

adv_data

string

Advertisement data (typically built with BLE.AdvBuilder.build())

scan_rsp

string

Scan response data (typically built with BLE.AdvBuilder.build()). Scan response is sent when a scanner performs an active scan. Optional

Returns: true if the advertisement was successfully queued for broadcast, false if the data is invalid, exceeds size limits, or there is an error.

note

The advertisement will broadcast for exactly 3 seconds. Advertising is shared system-wide across all scripts. If multiple scripts attempt to broadcast simultaneously, the last script to call advertiseOnce() will preempt any currently active advertisement.

BTHome support

Since version 2.0.0

A global BTHome object provides utilities for parsing and building BTHome protocol data. BTHome is a BLE broadcast format for sensor data that is commonly used in home automation. Scripts can parse incoming BTHome advertisements and construct BTHome payloads for broadcasting.

note

BTHome support is only available on devices that include BTHome functionality.

BTHome.parseData()

Parses BTHome service data and returns an array of decoded values.

Syntax:

BTHome.parseData(data[, addr[, key]]) -> array or null
PropertyTypeDescription

data

string

BTHome service data to parse

addr

string

BTHome device address (required only if data is encrypted) Optional

key

string

AES encryption key as 128-bit hexadecimal string (required only if data is encrypted) Optional

Returns: Array of BTHomeValue objects representing the decoded data, or null if parsing fails. If invoked with invalid arguments the script is aborted.

Each BTHomeValue object in the returned array has the following properties:

PropertyTypeDescription

type

number

Value type constant (see BTHomeValue type constants below)

objID

number

BTHome object ID

name

string

Human-readable name of the BTHome object

idx

number

Object index (for multiple instances of the same object type)

val

number or boolean or string

The decoded value (type depends on the BTHome object)

BTHomeValue type constants

  • BTHomeValue.UNKNOWN = 0
  • BTHomeValue.SENSOR = 1
  • BTHomeValue.BINARY_SENSOR = 2
  • BTHomeValue.RAW_SENSOR = 3
  • BTHomeValue.EVENT = 4
  • BTHomeValue.OTHER = 5

BTHome.DataBuilder

The BTHome.DataBuilder object provides methods for constructing BTHome payloads. It maintains internal state and allows building payloads incrementally.

note

The BTHome.DataBuilder is a singleton object within each script. Always call reset() before building a new payload to clear previous state.

BTHome.DataBuilder.addObject()

Adds a BTHome object to the payload being built.

Syntax:

BTHome.DataBuilder.addObject(objID, value) -> boolean
PropertyTypeDescription

objID

number

BTHome object ID to add

value

number or boolean or string

Value for the object (type must match the object's expected type)

Returns: true if the object was added successfully, false if the value type is invalid for the specified object ID.

note

BTHome object IDs and their expected value types are defined by the BTHome specification. Consult the specification for the complete list of supported object IDs. Text and raw BTHome objects have a maximum length of 3 characters/bytes in the current implementation.

BTHome.DataBuilder.setTriggerBased()

Marks the payload as trigger-based (event-driven) rather than state-based.

Syntax:

BTHome.DataBuilder.setTriggerBased() -> undefined

BTHome.DataBuilder.build()

Builds and returns the BTHome payload as an unencrypted string.

Syntax:

BTHome.DataBuilder.build() -> string

Returns: The constructed BTHome payload as a string.

BTHome.DataBuilder.buildEncrypted()

Builds and returns an encrypted BTHome payload.

Syntax:

BTHome.DataBuilder.buildEncrypted(key) -> string or null
PropertyTypeDescription

key

string

AES encryption key as 128-bit hexadecimal string

Returns: The constructed encrypted BTHome payload as a string, or null if encryption fails.

note

BTHome standard specifies AES CCM for encrypting BTHome data using device MAC address and a counter for ensuring uniqueness of the AES CCM IV. In this implementation the device bluetooth address and a global monotonic counter are used. The counter although rarely may wrap around and needs to be reset and also care should be taken not to reuse the same keys in this case. Facilities to manage the counter are provided in the global BTHome component, but it remains user responsibility.

BTHome.DataBuilder.reset()

Resets the builder state, clearing all added objects.

Syntax:

BTHome.DataBuilder.reset() -> undefined

Complete Examples

Temperature Sensor Monitor

// Monitor BLE temperature sensors
BLE.Scanner.start({
duration_ms: BLE.Scanner.INFINITE_SCAN,
active: false
}, function(event, result) {
if (event !== BLE.Scanner.SCAN_RESULT) return;

// Check for temperature service
if (BLE.GAP.hasService(result.advData, "181a")) {
let serviceData = BLE.GAP.parseServiceData(result.advData, "181a");
if (serviceData.length >= 2) {
// Parse temperature (example format)
let temp = (serviceData.charCodeAt(0) | (serviceData.charCodeAt(1) << 8)) / 100;
console.log("Temperature from", result.addr, ":", temp, "°C");
}
}
});

iBeacon Scanner

// Scan for iBeacons
BLE.Scanner.subscribe(function(event, result) {
if (event !== BLE.Scanner.SCAN_RESULT) return;

let mfgData = BLE.GAP.parseManufacturerData(result.advData);

// Check for Apple iBeacon (0x004C)
if (mfgData.length >= 23 &&
mfgData.charCodeAt(0) === 0x4C &&
mfgData.charCodeAt(1) === 0x00) {

// Extract UUID (bytes 4-19)
let uuid = "";
for (let i = 4; i < 20; i++) {
uuid += ("0" + mfgData.charCodeAt(i).toString(16)).slice(-2);
if (i === 7 || i === 9 || i === 11 || i === 13) uuid += "-";
}

// Extract major/minor
let major = (mfgData.charCodeAt(20) << 8) | mfgData.charCodeAt(21);
let minor = (mfgData.charCodeAt(22) << 8) | mfgData.charCodeAt(23);

console.log("iBeacon:", uuid, "Major:", major, "Minor:", minor, "RSSI:", result.rssi);
}
});

BLE.Scanner.start({duration_ms: BLE.Scanner.INFINITE_SCAN});

Best Practices with Filters

Filter by MAC Address

// Efficiently scan for specific devices using filters (firmware 2.0.0+)
BLE.Scanner.start({
duration_ms: 60000,
active: false,
filters: [
{
addrs: ["AA:BB:CC:DD:EE:FF", "11:22:33:44:55:66"]
}
]
}, function(event, result) {
if (event === BLE.Scanner.SCAN_RESULT) {
console.log("Target device found:", result.addr);
console.log("RSSI:", result.rssi);
}
});

Filter by Device Name Pattern

// Find all Shelly BLU devices using name wildcard
BLE.Scanner.start({
duration_ms: 30000,
active: true, // Active scan needed to get device names
filters: [
{
name: "SBDW*" // Matches SBDW-002C, etc.
},
{
name: "SBHT*" // Matches SBHT-003C, etc.
}
]
}, function(event, result) {
if (event === BLE.Scanner.SCAN_RESULT && result.local_name) {
console.log("Found Shelly BLU device:", result.local_name);
}
});

Filter by Service UUID

// Find devices advertising specific services
BLE.Scanner.start({
duration_ms: BLE.Scanner.INFINITE_SCAN,
rssi_thr: -80, // Only nearby devices
filters: [
{
services: ["181a"] // Environmental Sensing Service
},
{
services: ["180f"] // Battery Service
}
]
}, function(event, result) {
if (event === BLE.Scanner.SCAN_RESULT) {
console.log("Device with service:", result.addr);
// Check which service was found
if (BLE.GAP.hasService(result.advData, "181a")) {
console.log("- Has Environmental Sensing");
}
if (BLE.GAP.hasService(result.advData, "180f")) {
console.log("- Has Battery Service");
}
}
});

Filter by Manufacturer Data

// Find Apple devices (iBeacons, AirTags, etc.)
BLE.Scanner.start({
duration_ms: 30000,
filters: [
{
manufacturerData: {
companyIdentifier: 0x004C // Apple Inc.
}
}
]
}, function(event, result) {
if (event === BLE.Scanner.SCAN_RESULT) {
let mfgData = BLE.GAP.parseManufacturerData(result.advData);
if (mfgData && mfgData[0x004C]) {
console.log("Apple device found:", result.addr);
// Parse Apple-specific data
}
}
});

Combined Filters for BTHome Devices

// Scan for BTHome v2 devices with temperature data
BLE.Scanner.start({
duration_ms: BLE.Scanner.INFINITE_SCAN,
rssi_thr: -75,
filters: [
{
serviceData: {
service: "fcd2", // BTHome service UUID
dataPrefix: "\x40\x02", // BTHome v2 + temperature object
mask: "\xFF\xFF" // Exact match for these bytes
}
}
]
}, function(event, result) {
if (event === BLE.Scanner.SCAN_RESULT) {
let serviceData = BLE.GAP.parseServiceData(result.advData, "fcd2");
if (serviceData) {
// Parse BTHome temperature data
console.log("BTHome temperature sensor:", result.addr);
}
}
});

Scan Performance Optimization

Power-Efficient Scanning

// Low-power continuous monitoring
BLE.Scanner.start({
duration_ms: BLE.Scanner.INFINITE_SCAN,
active: false, // Passive scan uses less power
interval_ms: 1000, // Longer interval
window_ms: 50, // Shorter window
rssi_thr: -70, // Filter weak signals
filters: [
{
services: ["181a"] // Only environmental sensors
}
]
});

High-Performance Scanning

// Fast device discovery
BLE.Scanner.start({
duration_ms: 10000,
active: true, // Get complete device info
interval_ms: 100, // Short interval
window_ms: 100, // Maximum window
rssi_thr: 0 // No RSSI filtering
});

Balanced Scanning for Multiple Devices

// Good balance for monitoring multiple device types
BLE.Scanner.start({
duration_ms: BLE.Scanner.INFINITE_SCAN,
active: false,
interval_ms: 241, // Default balanced interval
window_ms: 61, // Default balanced window
rssi_thr: -80,
filters: [
{ name: "SBHT*" }, // Shelly BLU H&T
{ name: "SBDW*" }, // Shelly BLU Door/Window
{ services: ["181a"] }, // Generic temperature sensors
{ services: ["180f"] } // Battery-powered devices
]
}, function(event, result) {
if (event === BLE.Scanner.SCAN_RESULT) {
// Process different device types
if (result.local_name && result.local_name.startsWith("SBHT")) {
handleShellyHT(result);
} else if (BLE.GAP.hasService(result.advData, "181a")) {
handleGenericSensor(result);
}
}
});

function handleShellyHT(result) {
console.log("Shelly H&T:", result.addr);
}

function handleGenericSensor(result) {
console.log("Generic sensor:", result.addr);
}

Advertising Examples

Simple BTHome sensor data advertisement

BTHome.DataBuilder.reset();
BTHome.DataBuilder.addObject(0x02, 23.5); // Temperature
BTHome.DataBuilder.addObject(0x03, 65); // Humidity
let bthomePayload = BTHome.DataBuilder.build();

BLE.AdvBuilder.reset();
if (BLE.AdvBuilder.addBTHomeServiceData(bthomePayload)) {
let advData = BLE.AdvBuilder.build();
if (BLE.advertiseOnce(advData)) {
print("BTHome advertisement sent for 3 seconds");
}
}
BLE.AdvBuilder.reset();
BLE.AdvBuilder.addServiceData("180f", "\x64"); // Battery service: 100%
let mainAdv = BLE.AdvBuilder.build();

BLE.AdvBuilder.reset();
BLE.AdvBuilder.addName("MyShelly", true);
let scanRsp = BLE.AdvBuilder.build();

BLE.advertiseOnce(mainAdv, scanRsp);

Periodic sensor beacon (every 30 seconds)

Timer.set(30000, true, function() {
let temp = Shelly.getComponentStatus("temperature", 0).tC;

BTHome.DataBuilder.reset();
BTHome.DataBuilder.addObject(0x02, temp);
let payload = BTHome.DataBuilder.build();

BLE.AdvBuilder.reset();
BLE.AdvBuilder.addBTHomeServiceData(payload);
let adv = BLE.AdvBuilder.build();

if (BLE.advertiseOnce(adv)) {
print("Temperature beacon sent:", temp, "°C");
}
});

Custom manufacturer data

BLE.AdvBuilder.reset();
let customData = "\x01\x02\x03\x04"; // Custom payload
if (BLE.AdvBuilder.addShellyManufacturerData(customData)) {
let adv = BLE.AdvBuilder.build();
BLE.advertiseOnce(adv);
}

BTHome Examples

Parse incoming BTHome data from a BLE scan

BLE.Scanner.subscribe(function(event, result) {
if (event === BLE.Scanner.SCAN_RESULT) {
// Check if this is a BTHome device
if (result.service_data && result.service_data["fcd2"]) {
let bthomeData = result.service_data["fcd2"];

// Parse the BTHome data
let values = BTHome.parseData(bthomeData);

if (values !== null) {
// Process each value
for (let i = 0; i < values.length; i++) {
let v = values[i];
print("BTHome object:", v.name, "=", v.val);

// Example: react to temperature reading
if (v.name === "temperature") {
print("Temperature:", v.val, "°C");
}
}
}
}
}
});

Build a BTHome payload

BTHome.DataBuilder.reset();
BTHome.DataBuilder.addObject(0x02, 23.5); // Temperature
BTHome.DataBuilder.addObject(0x03, 65); // Humidity
BTHome.DataBuilder.addObject(0x15, true); // Motion detected

// Get unencrypted payload
let payload = BTHome.DataBuilder.build();
print("BTHome payload:", btoh(payload));

// Or build encrypted payload
let encryptedPayload = BTHome.DataBuilder.buildEncrypted("0123456789abcdef0123456789abcdef");
if (encryptedPayload !== null) {
print("Encrypted payload:", btoh(encryptedPayload));
}

Performance Considerations

Resource Usage
  • BLE scanning is CPU and memory intensive
  • Filter results to specific devices when possible
  • Use passive scanning when active scanning isn't needed
  • Consider using intervals for periodic scanning instead of continuous
  • Results are deduplicated for 3 seconds to reduce load

Version Notes

  • v0.12.0: Initial BLE support
  • v1.0.0: 3-second deduplication added
  • v1.5.0: Multiple script scan support
  • v1.6.0: Bluetooth start/stop without reboot
  • v1.7.0: CPU throttling at 25% usage
  • v2.0.0: Advertising and BTHome support