Integrando una báscula Bluetooth Swisshome con Home Assistant usando ESP32 y ESPHome
¡Hola a todos!
Quería compartir cómo integré mi báscula Bluetooth Swisshome con Home Assistant utilizando un ESP32 y ESPHome.
Lo mejor de todo es que esta báscula económica no necesita ninguna aplicación adicional: basta con interceptar las emisiones Bluetooth que lanza, y enviarlas a Home Assistant.
¿Qué hace esta báscula?
Esta báscula transmite paquetes de datos por Bluetooth, que incluyen:
- El peso corporal
- La impedancia corporal
Cada uno de estos datos se identifica por un tipo de paquete y necesita una operación XOR para decodificarse correctamente.
Pasos clave del proyecto
Decodificación de los datos
La báscula emite dos tipos de paquetes:
0xAD
→ para el peso corporal0xA6
→ para la impedancia
Ambos necesitan aplicar una operación XOR con una clave derivada del Company ID (0xA0AC
) para extraer los valores.
Configurando el ESP32 con ESPHome
Se usa ESPHome para configurar el ESP32, conectarlo a la red y escanear dispositivos Bluetooth cercanos.
A continuación te comparto un ejemplo del código que utilicé:
sensor:
- platform: template
name: "Peso Báscula"
id: weight_sensor
unit_of_measurement: "kg"
accuracy_decimals: 2
icon: "mdi:weight-kilogram"
- platform: template
name: "Impedancia (Z)"
id: body_impedancia_sensor
unit_of_measurement: "Ω"
accuracy_decimals: 0
icon: "mdi:scale-bathroom"
esp32_ble_tracker:
scan_parameters:
interval: 1100ms
window: 1100ms
active: true
on_ble_advertise:
- mac_address:
- XX:XX:XX:XX:XX:XX # MAC address de tu báscula
then:
- lambda: |-
const uint16_t company_id = 0xA0AC;
int xor_key = company_id >> 8;
for (auto data : x.get_manufacturer_datas()) {
if (data.data.size() >= 12) {
std::vector<uint8_t> buf(6);
for (int i = 0; i < 6; i++) {
buf[i] = data.data[i + 6] ^ xor_key;
}
int chk = 0;
for (int i = 0; i < 5; i++) {
chk += buf[i];
}
if ((chk & 0x1F) != (buf[5] & 0x1F)) {
ESP_LOGD("ble_adv", "Checksum error");
return;
}
uint8_t packet_type = buf[4];
if (packet_type == 0xAD) {
int32_t value = (buf[3] | (buf[2] << 8) | (buf[1] << 16) | (buf[0] << 24));
uint8_t state = (value >> 31) & 0x1;
int grams = value & 0x3FFFF;
float weight_kg = grams / 1000.0;
ESP_LOGD("ble_adv", "Peso: %.2f kg, state: %d", weight_kg, state);
if (state != 0) {
ESP_LOGD("ble_adv", "Medición completada.");
id(weight_sensor).publish_state(weight_kg);
}
} else if (packet_type == 0xA6) {
int fat_raw = ((buf[1]) << 8) | buf[0];
ESP_LOGD("ble_adv", "Impedancia: %d", fat_raw);
float impedancia = fat_raw / 10.0;
ESP_LOGD("ble_adv", "Impedancia (Z): %.2f Ω", impedancia);
id(body_impedancia_sensor).publish_state(impedancia);
} else {
ESP_LOGD("ble_adv", "Tipo de paquete no admitido: 0x%02X", packet_type);
}
}
}
💬 Anímate a dejar tu duda, sugerencia o comentario relacionado con este artículo. ¡Te leo!