Initial iBeacon rotator
This commit is contained in:
90
components/ibeacon_rotator/__init__.py
Normal file
90
components/ibeacon_rotator/__init__.py
Normal file
@@ -0,0 +1,90 @@
|
||||
import re
|
||||
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import esp32_ble
|
||||
from esphome.components.esp32 import add_idf_sdkconfig_option
|
||||
from esphome.components.esp32_ble import CONF_BLE_ID
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID
|
||||
from esphome.core import TimePeriod
|
||||
|
||||
AUTO_LOAD = ["esp32_ble"]
|
||||
DEPENDENCIES = ["esp32"]
|
||||
|
||||
ibeacon_rotator_ns = cg.esphome_ns.namespace("ibeacon_rotator")
|
||||
IBeaconRotator = ibeacon_rotator_ns.class_("IBeaconRotator", cg.Component)
|
||||
|
||||
CONF_UUID_PREFIX = "uuid_prefix"
|
||||
CONF_BROADCAST_LENGTH = "broadcast_length"
|
||||
CONF_MIN_INTERVAL = "min_interval"
|
||||
CONF_MAX_INTERVAL = "max_interval"
|
||||
CONF_MAJOR = "major"
|
||||
CONF_MINOR = "minor"
|
||||
CONF_MEASURED_POWER = "measured_power"
|
||||
|
||||
|
||||
def validate_uuid_prefix(value):
|
||||
if not isinstance(value, str):
|
||||
raise cv.Invalid("uuid_prefix must be a string of hex bytes")
|
||||
stripped = re.sub(r"[:\-\s]", "", value)
|
||||
if len(stripped) != 20:
|
||||
raise cv.Invalid(
|
||||
f"uuid_prefix must be exactly 10 bytes (20 hex chars after stripping separators); got {len(stripped)}"
|
||||
)
|
||||
if not re.fullmatch(r"[0-9A-Fa-f]+", stripped):
|
||||
raise cv.Invalid("uuid_prefix contains non-hex characters")
|
||||
return [int(stripped[i : i + 2], 16) for i in range(0, 20, 2)]
|
||||
|
||||
|
||||
def validate_config(config):
|
||||
if config[CONF_MIN_INTERVAL] > config[CONF_MAX_INTERVAL]:
|
||||
raise cv.Invalid("min_interval must be <= max_interval")
|
||||
return config
|
||||
|
||||
|
||||
_INTERVAL = cv.All(
|
||||
cv.positive_time_period_milliseconds,
|
||||
cv.Range(min=TimePeriod(milliseconds=20), max=TimePeriod(milliseconds=10240)),
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(IBeaconRotator),
|
||||
cv.GenerateID(CONF_BLE_ID): cv.use_id(esp32_ble.ESP32BLE),
|
||||
cv.Required(CONF_UUID_PREFIX): validate_uuid_prefix,
|
||||
cv.Optional(
|
||||
CONF_BROADCAST_LENGTH, default="30s"
|
||||
): cv.positive_time_period_milliseconds,
|
||||
cv.Optional(CONF_MIN_INTERVAL, default="100ms"): _INTERVAL,
|
||||
cv.Optional(CONF_MAX_INTERVAL, default="100ms"): _INTERVAL,
|
||||
cv.Optional(CONF_MAJOR, default=0): cv.uint16_t,
|
||||
cv.Optional(CONF_MINOR, default=0): cv.uint16_t,
|
||||
cv.Optional(CONF_MEASURED_POWER, default=-59): cv.int_range(
|
||||
min=-128, max=0
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
validate_config,
|
||||
)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = esp32_ble.validate_variant
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
prefix_arr = [cg.RawExpression(f"0x{b:02X}") for b in config[CONF_UUID_PREFIX]]
|
||||
var = cg.new_Pvariable(config[CONF_ID], prefix_arr)
|
||||
|
||||
parent = await cg.get_variable(config[CONF_BLE_ID])
|
||||
esp32_ble.register_gap_event_handler(parent, var)
|
||||
|
||||
await cg.register_component(var, config)
|
||||
cg.add(var.set_broadcast_length(config[CONF_BROADCAST_LENGTH]))
|
||||
cg.add(var.set_min_interval(config[CONF_MIN_INTERVAL]))
|
||||
cg.add(var.set_max_interval(config[CONF_MAX_INTERVAL]))
|
||||
cg.add(var.set_major(config[CONF_MAJOR]))
|
||||
cg.add(var.set_minor(config[CONF_MINOR]))
|
||||
cg.add(var.set_measured_power(config[CONF_MEASURED_POWER]))
|
||||
|
||||
add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True)
|
||||
add_idf_sdkconfig_option("CONFIG_BT_BLE_42_FEATURES_SUPPORTED", True)
|
||||
Reference in New Issue
Block a user