[ble_device_base] Migrate BLE sensor platforms to the neutral layer (batch 2: atc_mithermometer, pvvx_mithermometer, bthome_mithermometer) (#17950)

This commit is contained in:
Edvard Filistovič
2026-08-07 12:48:18 -05:00
committed by GitHub
parent b326cefea7
commit 77bfda6f1f
25 changed files with 177 additions and 95 deletions
@@ -1,8 +1,6 @@
#include "atc_mithermometer.h"
#include "esphome/core/log.h"
#ifdef USE_ESP32
namespace esphome::atc_mithermometer {
static const char *const TAG = "atc_mithermometer";
@@ -15,7 +13,7 @@ void ATCMiThermometer::dump_config() {
LOG_SENSOR(" ", "Battery Voltage", this->battery_voltage_);
}
bool ATCMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
bool ATCMiThermometer::parse_device(const ble_device_base::ESPBTDevice &device) {
if (device.address_uint64() != this->address_) {
ESP_LOGVV(TAG, "parse_device(): unknown MAC address.");
return false;
@@ -52,7 +50,7 @@ bool ATCMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &device
return success;
}
optional<ParseResult> ATCMiThermometer::parse_header_(const esp32_ble_tracker::ServiceData &service_data) {
optional<ParseResult> ATCMiThermometer::parse_header_(const ble_device_base::ServiceData &service_data) {
ParseResult result;
if (!service_data.uuid.contains(0x1A, 0x18)) {
ESP_LOGVV(TAG, "parse_header(): no service data UUID magic bytes.");
@@ -132,5 +130,3 @@ bool ATCMiThermometer::report_results_(const optional<ParseResult> &result, cons
}
} // namespace esphome::atc_mithermometer
#endif
@@ -2,12 +2,10 @@
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/ble_device_base/ble_device.h"
#include <vector>
#ifdef USE_ESP32
namespace esphome::atc_mithermometer {
struct ParseResult {
@@ -18,11 +16,11 @@ struct ParseResult {
int raw_offset;
};
class ATCMiThermometer final : public Component, public esp32_ble_tracker::ESPBTDeviceListener {
class ATCMiThermometer final : public Component, public ble_device_base::ESPBTDeviceListener {
public:
void set_address(uint64_t address) { address_ = address; };
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
bool parse_device(const ble_device_base::ESPBTDevice &device) override;
void dump_config() override;
void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; }
void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; }
@@ -40,11 +38,9 @@ class ATCMiThermometer final : public Component, public esp32_ble_tracker::ESPBT
uint8_t last_frame_count_{0};
optional<ParseResult> parse_header_(const esp32_ble_tracker::ServiceData &service_data);
optional<ParseResult> parse_header_(const ble_device_base::ServiceData &service_data);
bool parse_message_(const std::vector<uint8_t> &message, ParseResult &result);
bool report_results_(const optional<ParseResult> &result, const char *address);
};
} // namespace esphome::atc_mithermometer
#endif
@@ -1,5 +1,5 @@
import esphome.codegen as cg
from esphome.components import esp32_ble_tracker, sensor
from esphome.components import ble_device_base, sensor
import esphome.config_validation as cv
from esphome.const import (
CONF_BATTERY_LEVEL,
@@ -24,14 +24,15 @@ from esphome.const import (
CODEOWNERS = ["@ahpohl"]
DEPENDENCIES = ["esp32_ble_tracker"]
AUTO_LOAD = ["ble_device_base"]
atc_mithermometer_ns = cg.esphome_ns.namespace("atc_mithermometer")
ATCMiThermometer = atc_mithermometer_ns.class_(
"ATCMiThermometer", esp32_ble_tracker.ESPBTDeviceListener, cg.Component
"ATCMiThermometer", ble_device_base.ESPBTDeviceListener, cg.Component
)
CONFIG_SCHEMA = (
CONFIG_SCHEMA = cv.All(
ble_device_base.rename_legacy_hub_id("atc_mithermometer"),
cv.Schema(
{
cv.GenerateID(): cv.declare_id(ATCMiThermometer),
@@ -71,15 +72,15 @@ CONFIG_SCHEMA = (
),
}
)
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
.extend(cv.COMPONENT_SCHEMA)
.extend(ble_device_base.BLE_DEVICE_SCHEMA),
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await esp32_ble_tracker.register_ble_device(var, config)
await ble_device_base.register_ble_device(var, config)
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
@@ -1,24 +1,24 @@
import esphome.codegen as cg
from esphome.components import esp32_ble_tracker
from esphome.components import ble_device_base
import esphome.config_validation as cv
from esphome.const import CONF_BINDKEY, CONF_ID, CONF_MAC_ADDRESS
from esphome.core import HexInt
CODEOWNERS = ["@nagyrobi"]
DEPENDENCIES = ["esp32_ble_tracker"]
AUTO_LOAD = ["ble_device_base"]
BLE_DEVICE_SCHEMA = esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA
bthome_mithermometer_ns = cg.esphome_ns.namespace("bthome_mithermometer")
BTHomeMiThermometer = bthome_mithermometer_ns.class_(
"BTHomeMiThermometer", esp32_ble_tracker.ESPBTDeviceListener, cg.Component
"BTHomeMiThermometer", ble_device_base.ESPBTDeviceListener, cg.Component
)
def bthome_mithermometer_base_schema(extra_schema=None):
if extra_schema is None:
extra_schema = {}
return (
return cv.All(
ble_device_base.rename_legacy_hub_id("bthome_mithermometer"),
cv.Schema(
{
cv.GenerateID(CONF_ID): cv.declare_id(BTHomeMiThermometer),
@@ -26,15 +26,15 @@ def bthome_mithermometer_base_schema(extra_schema=None):
cv.Optional(CONF_BINDKEY): cv.bind_key,
}
)
.extend(BLE_DEVICE_SCHEMA)
.extend(cv.COMPONENT_SCHEMA)
.extend(extra_schema)
.extend(ble_device_base.BLE_DEVICE_SCHEMA),
)
async def setup_bthome_mithermometer(var, config):
await cg.register_component(var, config)
await esp32_ble_tracker.register_ble_device(var, config)
await ble_device_base.register_ble_device(var, config)
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
if bindkey := config.get(CONF_BINDKEY):
bindkey_bytes = [
@@ -8,13 +8,20 @@
#include <cstring>
#include <span>
// AES-CCM backend for encrypted-advertisement (bindkey) decryption:
// - ESP32 + ESP-IDF >= 6.0 -> PSA crypto (psa_aead_decrypt), hardware-backed.
// - every other platform -> the portable software AES-CCM in ble_device_base, so
// decryption never depends on the SDK exposing mbedtls/PSA to application code
// (e.g. LibreTiny beken-72xx keeps its mbedtls internal). Works on any BLE platform.
#ifdef USE_ESP32
#include <esp_idf_version.h>
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
#include <psa/crypto.h>
#else
#include "mbedtls/ccm.h"
#define BTHOME_CRYPTO_PSA
#endif
#endif
#ifndef BTHOME_CRYPTO_PSA
#include "esphome/components/ble_device_base/ble_aes_ccm.h"
#endif
namespace esphome::bthome_mithermometer {
@@ -157,7 +164,7 @@ void BTHomeMiThermometer::dump_config() {
LOG_SENSOR(" ", "Signal Strength", this->signal_strength_);
}
bool BTHomeMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
bool BTHomeMiThermometer::parse_device(const ble_device_base::ESPBTDevice &device) {
bool matched = false;
for (auto &service_data : device.get_service_datas()) {
if (this->handle_service_data_(service_data, device)) {
@@ -204,7 +211,7 @@ bool BTHomeMiThermometer::decrypt_bthome_payload_(const std::vector<uint8_t> &da
const uint8_t *ciphertext = data.data() + 1;
const uint8_t *mic = data.data() + data.size() - BTHOME_MIC_SIZE;
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
#if defined(BTHOME_CRYPTO_PSA)
// PSA AEAD expects ciphertext + tag concatenated
// BLE advertisement max payload is 31 bytes, so this is always sufficient
static constexpr size_t MAX_CT_WITH_TAG = 32;
@@ -236,29 +243,18 @@ bool BTHomeMiThermometer::decrypt_bthome_payload_(const std::vector<uint8_t> &da
return false;
}
#else
mbedtls_ccm_context ctx;
mbedtls_ccm_init(&ctx);
int ret = mbedtls_ccm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, this->bindkey_, BTHOME_BINDKEY_SIZE * 8);
if (ret) {
ESP_LOGVV(TAG, "mbedtls_ccm_setkey() failed.");
mbedtls_ccm_free(&ctx);
return false;
}
ret = mbedtls_ccm_auth_decrypt(&ctx, ciphertext_size, nonce.data(), nonce.size(), nullptr, 0, ciphertext,
payload.data(), mic, BTHOME_MIC_SIZE);
mbedtls_ccm_free(&ctx);
if (ret) {
ESP_LOGVV(TAG, "BTHome decryption failed (ret=%d).", ret);
// Portable software AES-CCM (ble_device_base) — no SDK mbedtls/PSA dependency.
if (!ble_device_base::aes_ccm_auth_decrypt(this->bindkey_, nonce.data(), nonce.size(), nullptr, 0, ciphertext,
ciphertext_size, payload.data(), mic, BTHOME_MIC_SIZE)) {
ESP_LOGVV(TAG, "BTHome decryption failed.");
return false;
}
#endif
return true;
}
bool BTHomeMiThermometer::handle_service_data_(const esp32_ble_tracker::ServiceData &service_data,
const esp32_ble_tracker::ESPBTDevice &device) {
bool BTHomeMiThermometer::handle_service_data_(const ble_device_base::ServiceData &service_data,
const ble_device_base::ESPBTDevice &device) {
if (!service_data.uuid.contains(0xD2, 0xFC)) {
return false;
}
@@ -439,5 +435,3 @@ bool BTHomeMiThermometer::handle_service_data_(const esp32_ble_tracker::ServiceD
}
} // namespace esphome::bthome_mithermometer
#endif
@@ -1,6 +1,6 @@
#pragma once
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/ble_device_base/ble_device.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h"
@@ -8,11 +8,12 @@
#include <initializer_list>
#include <vector>
#ifdef USE_ESP32
// No platform #ifdef: ble_device_base provides the BLE types on every platform; this
// component is only compiled when configured (which requires a BLE hub). bindkey (AES-CCM)
// decryption availability is selected per platform in the .cpp.
namespace esphome::bthome_mithermometer {
class BTHomeMiThermometer final : public esp32_ble_tracker::ESPBTDeviceListener, public Component {
class BTHomeMiThermometer final : public ble_device_base::ESPBTDeviceListener, public Component {
public:
void set_address(uint64_t address) { this->address_ = address; }
void set_bindkey(std::initializer_list<uint8_t> bindkey);
@@ -24,11 +25,11 @@ class BTHomeMiThermometer final : public esp32_ble_tracker::ESPBTDeviceListener,
void set_signal_strength(sensor::Sensor *signal_strength) { this->signal_strength_ = signal_strength; }
void dump_config() override;
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
bool parse_device(const ble_device_base::ESPBTDevice &device) override;
protected:
bool handle_service_data_(const esp32_ble_tracker::ServiceData &service_data,
const esp32_ble_tracker::ESPBTDevice &device);
bool handle_service_data_(const ble_device_base::ServiceData &service_data,
const ble_device_base::ESPBTDevice &device);
bool decrypt_bthome_payload_(const std::vector<uint8_t> &data, uint64_t source_address,
std::vector<uint8_t> &payload) const;
@@ -45,5 +46,3 @@ class BTHomeMiThermometer final : public esp32_ble_tracker::ESPBTDeviceListener,
};
} // namespace esphome::bthome_mithermometer
#endif
@@ -23,9 +23,9 @@ from esphome.const import (
from . import bthome_mithermometer_base_schema, setup_bthome_mithermometer
CODEOWNERS = ["@nagyrobi"]
AUTO_LOAD = ["ble_device_base"]
DEPENDENCIES = ["esp32_ble_tracker"]
CODEOWNERS = ["@nagyrobi"]
CONFIG_SCHEMA = bthome_mithermometer_base_schema(
{
@@ -1,16 +1,17 @@
#include "pvvx_display.h"
#include "esphome/components/esp32_ble/ble_uuid.h"
#include "esphome/core/log.h"
#include "esphome/core/defines.h"
#ifdef USE_ESP32
#include "pvvx_display.h"
#include "esphome/components/ble_device_base/ble_device.h"
#include "esphome/core/log.h"
namespace esphome::pvvx_mithermometer {
static const char *const TAG = "display.pvvx_mithermometer";
void PVVXDisplay::dump_config() {
char service_buf[esp32_ble::UUID_STR_LEN];
char char_buf[esp32_ble::UUID_STR_LEN];
char service_buf[ble_device_base::UUID_STR_LEN];
char char_buf[ble_device_base::UUID_STR_LEN];
ESP_LOGCONFIG(TAG,
"PVVX MiThermometer display:\n"
" MAC address : %s\n"
@@ -188,4 +189,4 @@ void PVVXDisplay::sync_time_() {
} // namespace esphome::pvvx_mithermometer
#endif
#endif // USE_ESP32
@@ -1,13 +1,16 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#ifdef USE_ESP32
#include "esphome/core/component.h"
#include "esphome/components/ble_client/ble_client.h"
#include "esphome/components/ble_device_base/ble_device.h"
#include "esphome/components/display/display.h"
#include <cinttypes>
#ifdef USE_ESP32
#include <esp_gattc_api.h>
#ifdef USE_TIME
#include "esphome/components/time/real_time_clock.h"
@@ -121,14 +124,13 @@ class PVVXDisplay final : public ble_client::BLEClientNode, public PollingCompon
uint16_t char_handle_ = 0;
bool connection_established_ = false;
esp32_ble_tracker::ESPBTUUID service_uuid_ =
esp32_ble_tracker::ESPBTUUID::from_raw("00001f10-0000-1000-8000-00805f9b34fb");
esp32_ble_tracker::ESPBTUUID char_uuid_ =
esp32_ble_tracker::ESPBTUUID::from_raw("00001f1f-0000-1000-8000-00805f9b34fb");
ble_device_base::ESPBTUUID service_uuid_ =
ble_device_base::ESPBTUUID::from_raw("00001f10-0000-1000-8000-00805f9b34fb");
ble_device_base::ESPBTUUID char_uuid_ = ble_device_base::ESPBTUUID::from_raw("00001f1f-0000-1000-8000-00805f9b34fb");
pvvx_writer_t writer_{};
};
} // namespace esphome::pvvx_mithermometer
#endif
#endif // USE_ESP32
@@ -1,8 +1,6 @@
#include "pvvx_mithermometer.h"
#include "esphome/core/log.h"
#ifdef USE_ESP32
namespace esphome::pvvx_mithermometer {
static const char *const TAG = "pvvx_mithermometer";
@@ -15,7 +13,7 @@ void PVVXMiThermometer::dump_config() {
LOG_SENSOR(" ", "Battery Voltage", this->battery_voltage_);
}
bool PVVXMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
bool PVVXMiThermometer::parse_device(const ble_device_base::ESPBTDevice &device) {
if (device.address_uint64() != this->address_) {
ESP_LOGVV(TAG, "parse_device(): unknown MAC address.");
return false;
@@ -52,7 +50,7 @@ bool PVVXMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &devic
return success;
}
optional<ParseResult> PVVXMiThermometer::parse_header_(const esp32_ble_tracker::ServiceData &service_data) {
optional<ParseResult> PVVXMiThermometer::parse_header_(const ble_device_base::ServiceData &service_data) {
ParseResult result;
if (!service_data.uuid.contains(0x1A, 0x18)) {
ESP_LOGVV(TAG, "parse_header(): no service data UUID magic bytes.");
@@ -140,5 +138,3 @@ bool PVVXMiThermometer::report_results_(const optional<ParseResult> &result, con
}
} // namespace esphome::pvvx_mithermometer
#endif
@@ -2,12 +2,10 @@
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/ble_device_base/ble_device.h"
#include <vector>
#ifdef USE_ESP32
namespace esphome::pvvx_mithermometer {
struct ParseResult {
@@ -18,11 +16,11 @@ struct ParseResult {
int raw_offset;
};
class PVVXMiThermometer final : public Component, public esp32_ble_tracker::ESPBTDeviceListener {
class PVVXMiThermometer final : public Component, public ble_device_base::ESPBTDeviceListener {
public:
void set_address(uint64_t address) { address_ = address; };
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
bool parse_device(const ble_device_base::ESPBTDevice &device) override;
void dump_config() override;
void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; }
void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; }
@@ -40,11 +38,9 @@ class PVVXMiThermometer final : public Component, public esp32_ble_tracker::ESPB
uint8_t last_frame_count_{0};
optional<ParseResult> parse_header_(const esp32_ble_tracker::ServiceData &service_data);
optional<ParseResult> parse_header_(const ble_device_base::ServiceData &service_data);
bool parse_message_(const std::vector<uint8_t> &message, ParseResult &result);
bool report_results_(const optional<ParseResult> &result, const char *address);
};
} // namespace esphome::pvvx_mithermometer
#endif
@@ -1,5 +1,5 @@
import esphome.codegen as cg
from esphome.components import esp32_ble_tracker, sensor
from esphome.components import ble_device_base, sensor
import esphome.config_validation as cv
from esphome.const import (
CONF_BATTERY_LEVEL,
@@ -24,14 +24,15 @@ from esphome.const import (
CODEOWNERS = ["@pasiz"]
DEPENDENCIES = ["esp32_ble_tracker"]
AUTO_LOAD = ["ble_device_base"]
pvvx_mithermometer_ns = cg.esphome_ns.namespace("pvvx_mithermometer")
PVVXMiThermometer = pvvx_mithermometer_ns.class_(
"PVVXMiThermometer", esp32_ble_tracker.ESPBTDeviceListener, cg.Component
"PVVXMiThermometer", ble_device_base.ESPBTDeviceListener, cg.Component
)
CONFIG_SCHEMA = (
CONFIG_SCHEMA = cv.All(
ble_device_base.rename_legacy_hub_id("pvvx_mithermometer"),
cv.Schema(
{
cv.GenerateID(): cv.declare_id(PVVXMiThermometer),
@@ -71,15 +72,15 @@ CONFIG_SCHEMA = (
),
}
)
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
.extend(cv.COMPONENT_SCHEMA)
.extend(ble_device_base.BLE_DEVICE_SCHEMA),
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await esp32_ble_tracker.register_ble_device(var, config)
await ble_device_base.register_ble_device(var, config)
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
@@ -0,0 +1,7 @@
sensor:
- platform: atc_mithermometer
mac_address: A4:C1:38:4E:16:78
temperature:
name: ATC Temperature
humidity:
name: ATC Humidity
@@ -1,7 +1,10 @@
esp32_ble_tracker:
id: ble_tracker_hub
sensor:
# Explicit ble_hub_id: pins the neutral binding as a declared key.
- platform: atc_mithermometer
ble_hub_id: ble_tracker_hub
mac_address: A4:C1:38:4E:16:78
temperature:
name: ATC Temperature
@@ -0,0 +1,3 @@
packages:
ln882h_ble_tracker: !include ../ln882h_ble_tracker/common.yaml
atc_mithermometer: !include common-ln.yaml
@@ -0,0 +1,10 @@
# The esp32_ble_id -> ble_hub_id alias (removal 2027.2.0) still validates.
esp32_ble_tracker:
id: ble_tracker_hub
sensor:
- platform: atc_mithermometer
esp32_ble_id: ble_tracker_hub
mac_address: A4:C1:38:4E:16:78
temperature:
name: ATC Legacy Key Temperature
@@ -0,0 +1,17 @@
# Config-only: the CI base board (generic-bk7252, BLE 4.2) cannot compile the
# BLE 5.x tracker, so this fixture proves validation (schema + neutral binding)
# on a non-esp32 platform; codegen and compilation are not exercised here.
bk72xx_ble_tracker:
id: ble_hub
sensor:
- platform: atc_mithermometer
ble_hub_id: ble_hub
mac_address: A4:C1:38:4E:16:78
temperature:
name: BK ATC Temperature
# No ble_hub_id: exercises the generated binding real configs use.
- platform: atc_mithermometer
mac_address: A4:C1:38:4E:16:79
temperature:
name: BK ATC Implicit Temperature
@@ -0,0 +1,9 @@
sensor:
- platform: bthome_mithermometer
mac_address: A4:C1:38:4E:16:78
# bindkey compiles ble_device_base::aes_ccm_auth_decrypt off Espressif
bindkey: eef418daf699a0c188f3bfd17e4565d9
temperature:
name: BTHome Temperature
humidity:
name: BTHome Humidity
@@ -1,7 +1,10 @@
esp32_ble_tracker:
id: ble_tracker_hub
sensor:
# Explicit ble_hub_id: pins the neutral binding as a declared key.
- platform: bthome_mithermometer
ble_hub_id: ble_tracker_hub
mac_address: A4:C1:38:4E:16:78
bindkey: eef418daf699a0c188f3bfd17e4565d9
temperature:
@@ -0,0 +1,3 @@
packages:
ln882h_ble_tracker: !include ../ln882h_ble_tracker/common.yaml
bthome_mithermometer: !include common-ln.yaml
@@ -0,0 +1,18 @@
# Config-only: the CI base board (generic-bk7252, BLE 4.2) cannot compile the
# BLE 5.x tracker, so this fixture proves validation (schema + neutral binding)
# on a non-esp32 platform; codegen and compilation are not exercised here.
bk72xx_ble_tracker:
id: ble_hub
sensor:
- platform: bthome_mithermometer
ble_hub_id: ble_hub
mac_address: A4:C1:38:4E:16:78
bindkey: eef418daf699a0c188f3bfd17e4565d9
temperature:
name: BK BTHome Temperature
# No ble_hub_id: exercises the generated binding real configs use.
- platform: bthome_mithermometer
mac_address: A4:C1:38:4E:16:79
temperature:
name: BK BTHome Implicit Temperature
@@ -0,0 +1,8 @@
# Sensor only: the pvvx display is a GATT client and stays ESP32-only.
sensor:
- platform: pvvx_mithermometer
mac_address: A4:C1:38:4E:16:78
temperature:
name: PVVX Temperature
humidity:
name: PVVX Humidity
@@ -3,6 +3,7 @@ wifi:
password: password1
esp32_ble_tracker:
id: ble_tracker_hub
ble_client:
- mac_address: 01:02:03:04:05:06
@@ -26,7 +27,9 @@ display:
it.print_battery(true);
sensor:
# Explicit ble_hub_id: pins the neutral binding as a declared key.
- platform: pvvx_mithermometer
ble_hub_id: ble_tracker_hub
mac_address: A4:C1:38:4E:16:78
temperature:
name: PVVX Temperature
@@ -0,0 +1,3 @@
packages:
ln882h_ble_tracker: !include ../ln882h_ble_tracker/common.yaml
pvvx_mithermometer: !include common-ln.yaml
@@ -0,0 +1,13 @@
# Config-only: the CI base board (generic-bk7252, BLE 4.2) cannot compile the
# BLE 5.x tracker, so this fixture proves validation (schema + neutral binding)
# on a non-esp32 platform; codegen and compilation are not exercised here. The display platform is excluded: it needs ble_client
# (GATT), which only the esp32 tracker stack provides.
bk72xx_ble_tracker:
id: ble_hub
sensor:
- platform: pvvx_mithermometer
ble_hub_id: ble_hub
mac_address: A4:C1:38:4E:16:78
temperature:
name: BK PVVX Temperature