[BLE] Add BM6 Battery Monitor connection support (#2274)
* [BLE] Add BM6 Battery Monitor connection support Implement BLE connection support for BM6 Battery Monitor devices, following the BM2 pattern with BM6-specific protocol handling. Key features: - Encrypted command/response using AES-128 CBC - Read voltage, temperature, and State of Charge (SoC) - Home Assistant MQTT Discovery support - Device tracker integration Technical implementation: - Add BM6_connect class with encrypted command handling - Encryption key (static const): "leagend\xff\xfe0100009" - Parse hex string positions for voltage (15-17), temp (8-9), SoC (12-13) - Add discovery with 3 sensors (voltage, temperature, battery %) - Validate message signature (D15507) - Add canWrite() check before writing to characteristic - Empty stub for non-ESP32 builds Code quality improvements: - Extract AES key as static constant to avoid duplication - Use shared jsonVoltBM template for both BM2 and BM6 - Clean up commented-out reasoning in parsing logic Based on reverse engineering from: https://github.com/JeffWDH/bm6-battery-monitor Files modified: - main/gatewayBLEConnect.h: Add BM6_connect class - main/gatewayBLEConnect.cpp: Implement BM6 connection logic - main/gatewayBT.cpp: Add detection, connection, and discovery handlers - main/config_mqttDiscovery.h: Rename jsonVoltBM2 to jsonVoltBM Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Add BM6 to exceptions --------- Co-authored-by: Florian <1technophile@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
4 files changed
+184
-5
No files matched your search
+38
-4
@@ -514,7 +514,7 @@ void BM2Discovery(const char* mac, const char* sensorModel_id) {
|
||||
# define BM2parametersCount 2
|
||||
THEENGS_LOG_TRACE(F("BM2Discovery" CR));
|
||||
const char* BM2sensor[BM2parametersCount][9] = {
|
||||
{HASS_TYPE_SENSOR, "volt", mac, HASS_CLASS_VOLTAGE, jsonVoltBM2, "", "", HASS_UNIT_VOLT, stateClassMeasurement}, // We use a json definition that retrieve only data from the BM2 decoder, as this sensor also advertize volt as an iBeacon
|
||||
{HASS_TYPE_SENSOR, "volt", mac, HASS_CLASS_VOLTAGE, jsonVoltBM, "", "", HASS_UNIT_VOLT, stateClassMeasurement}, // We use a json definition that retrieve only data from the BM decoder, as this sensor also advertizes volt as an iBeacon
|
||||
{HASS_TYPE_SENSOR, "batt", mac, HASS_CLASS_BATTERY, jsonBatt, "", "", HASS_UNIT_PERCENT, stateClassMeasurement}
|
||||
//component type,name,availability topic,device class,value template,payload on, payload off, unit of measurement
|
||||
};
|
||||
@@ -522,6 +522,19 @@ void BM2Discovery(const char* mac, const char* sensorModel_id) {
|
||||
createDiscoveryFromList(mac, BM2sensor, BM2parametersCount, "BM2", "Generic", sensorModel_id);
|
||||
}
|
||||
|
||||
void BM6Discovery(const char* mac, const char* sensorModel_id) {
|
||||
# define BM6parametersCount 3
|
||||
THEENGS_LOG_TRACE(F("BM6Discovery" CR));
|
||||
const char* BM6sensor[BM6parametersCount][9] = {
|
||||
{HASS_TYPE_SENSOR, "volt", mac, HASS_CLASS_VOLTAGE, jsonVoltBM, "", "", HASS_UNIT_VOLT, stateClassMeasurement}, // We use a json definition that retrieve only data from the BM decoder, as this sensor also advertizes volt as an iBeacon
|
||||
{HASS_TYPE_SENSOR, "temp", mac, HASS_CLASS_TEMPERATURE, jsonTempc, "", "", HASS_UNIT_CELSIUS, stateClassMeasurement},
|
||||
{HASS_TYPE_SENSOR, "batt", mac, HASS_CLASS_BATTERY, jsonBatt, "", "", HASS_UNIT_PERCENT, stateClassMeasurement}
|
||||
//component type,name,availability topic,device class,value template,payload on, payload off, unit of measurement
|
||||
};
|
||||
|
||||
createDiscoveryFromList(mac, BM6sensor, BM6parametersCount, "BM6", "Generic", sensorModel_id);
|
||||
}
|
||||
|
||||
void LYWSD03MMCDiscovery(const char* mac, const char* sensorModel) {
|
||||
# define LYWSD03MMCparametersCount 4
|
||||
THEENGS_LOG_TRACE(F("LYWSD03MMCDiscovery" CR));
|
||||
@@ -585,6 +598,7 @@ void MHO_C401Discovery(const char* mac, const char* sensorModel) {}
|
||||
void HHCCJCY01HHCCDiscovery(const char* mac, const char* sensorModel) {}
|
||||
void DT24Discovery(const char* mac, const char* sensorModel_id) {}
|
||||
void BM2Discovery(const char* mac, const char* sensorModel_id) {}
|
||||
void BM6Discovery(const char* mac, const char* sensorModel_id) {}
|
||||
void XMWSDJ04MMCDiscovery(const char* mac, const char* sensorModel_id) {}
|
||||
# endif
|
||||
|
||||
@@ -747,6 +761,10 @@ void BLEconnect() {
|
||||
BM2_connect BLEclient(addr);
|
||||
BLEclient.processActions(BLEactions);
|
||||
BLEclient.publishData();
|
||||
} else if (p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::BM6) {
|
||||
BM6_connect BLEclient(addr);
|
||||
BLEclient.processActions(BLEactions);
|
||||
BLEclient.publishData();
|
||||
} else if (p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::HHCCJCY01HHCC) {
|
||||
HHCCJCY01HHCC_connect BLEclient(addr);
|
||||
BLEclient.processActions(BLEactions);
|
||||
@@ -782,6 +800,7 @@ void BLEconnect() {
|
||||
p->sensorModel_id != TheengsDecoder::BLE_ID_NUM::HHCCJCY01HHCC &&
|
||||
p->sensorModel_id != BLEconectable::id::LYWSD03MMC &&
|
||||
p->sensorModel_id != TheengsDecoder::BLE_ID_NUM::BM2 &&
|
||||
p->sensorModel_id != TheengsDecoder::BLE_ID_NUM::BM6 &&
|
||||
p->sensorModel_id != BLEconectable::id::MHO_C401 &&
|
||||
p->sensorModel_id != BLEconectable::id::XMWSDJ04MMC) {
|
||||
// if irregulary connected to and connection failed clear the connect flag.
|
||||
@@ -973,7 +992,9 @@ void launchBTDiscovery(bool overrideDiscovery) {
|
||||
if (!BTConfig.extDecoderEnable && // Do not decode if an external decoder is configured
|
||||
p->sensorModel_id > UNKWNON_MODEL &&
|
||||
p->sensorModel_id < TheengsDecoder::BLE_ID_NUM::BLE_ID_MAX &&
|
||||
p->sensorModel_id != TheengsDecoder::BLE_ID_NUM::HHCCJCY01HHCC && p->sensorModel_id != TheengsDecoder::BLE_ID_NUM::BM2) { // Exception on HHCCJCY01HHCC and BM2 as these ones are discoverable and connectable
|
||||
p->sensorModel_id != TheengsDecoder::BLE_ID_NUM::HHCCJCY01HHCC &&
|
||||
p->sensorModel_id != TheengsDecoder::BLE_ID_NUM::BM2 &&
|
||||
p->sensorModel_id != TheengsDecoder::BLE_ID_NUM::BM6) { // Exception on HHCCJCY01HHCC and BM2/BM6 as these ones are discoverable and connectable
|
||||
if (isTracker) {
|
||||
String tracker_name = String(model_id.c_str()) + "-tracker";
|
||||
String tracker_id = macWOdots + "-tracker";
|
||||
@@ -1124,7 +1145,7 @@ void launchBTDiscovery(bool overrideDiscovery) {
|
||||
} else {
|
||||
if ((p->sensorModel_id > BLEconectable::id::MIN &&
|
||||
p->sensorModel_id < BLEconectable::id::MAX) ||
|
||||
p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::HHCCJCY01HHCC || p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::BM2) {
|
||||
p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::HHCCJCY01HHCC || p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::BM2 || p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::BM6) {
|
||||
// Discovery of sensors from which we retrieve data only by connect
|
||||
if (p->sensorModel_id == BLEconectable::id::DT24_BLE) {
|
||||
DT24Discovery(macWOdots.c_str(), "DT24-BLE");
|
||||
@@ -1142,6 +1163,19 @@ void launchBTDiscovery(bool overrideDiscovery) {
|
||||
model.c_str(), brand.c_str(), model_id.c_str(), macWOdots.c_str(), false,
|
||||
stateClassNone);
|
||||
}
|
||||
if (p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::BM6) {
|
||||
// Sensor discovery
|
||||
BM6Discovery(macWOdots.c_str(), "BM6");
|
||||
// Device tracker discovery
|
||||
String tracker_id = macWOdots + "-tracker";
|
||||
createDiscovery(HASS_TYPE_DEVICE_TRACKER,
|
||||
discovery_topic.c_str(), "BM6-tracker", tracker_id.c_str(),
|
||||
will_Topic, "occupancy", "{% if value_json.get('rssi') -%}home{%- else -%}not_home{%- endif %}",
|
||||
"", "", "",
|
||||
0, "", "", false, "",
|
||||
model.c_str(), brand.c_str(), model_id.c_str(), macWOdots.c_str(), false,
|
||||
stateClassNone);
|
||||
}
|
||||
if (p->sensorModel_id == BLEconectable::id::LYWSD03MMC) {
|
||||
LYWSD03MMCDiscovery(macWOdots.c_str(), "LYWSD03MMC");
|
||||
}
|
||||
@@ -1429,7 +1463,7 @@ void process_bledata(JsonObject& BLEdata) {
|
||||
if ((BLEdata["type"].as<string>()).compare("RMAC") != 0 && model_id != TheengsDecoder::BLE_ID_NUM::IBEACON) { // Do not store in memory the random mac devices and iBeacons
|
||||
if (model_id >= 0) { // Broadcaster devices
|
||||
THEENGS_LOG_TRACE(F("Decoder found device: %s" CR), BLEdata["model_id"].as<const char*>());
|
||||
if (model_id == TheengsDecoder::BLE_ID_NUM::HHCCJCY01HHCC || model_id == TheengsDecoder::BLE_ID_NUM::BM2) { // Device that broadcast and can be connected
|
||||
if (model_id == TheengsDecoder::BLE_ID_NUM::HHCCJCY01HHCC || model_id == TheengsDecoder::BLE_ID_NUM::BM2 || model_id == TheengsDecoder::BLE_ID_NUM::BM6) { // Device that broadcast and can be connected
|
||||
createOrUpdateDevice(mac, device_flags_connect, model_id, mac_type, deviceName);
|
||||
} else {
|
||||
createOrUpdateDevice(mac, device_flags_init, model_id, mac_type, deviceName);
|
||||
|
||||
Reference in new issue
Block a user