1
0
mirror of https://github.com/esphome/esphome.git synced 2025-02-28 15:58:16 +00:00

Add tracker for OralB toothbrushes

The OralB toothbrushes expose some of their information in their bluetooth advertisement data.

This data lets us see the state (idle, running), brush mode (daily clean, tongue, whitening, etc.), pressure and some other bits of data.

This component lets you expose that data with config as follows:

```
esp32_ble_tracker:

sensor:
  - platform: oralb_brush
    mac_address: 00:00:00:00:00:00
    state:
      name: "Toothbrush State"
```

Checkout https://github.com/zewelor/bt-mqtt-gateway/blob/master/workers/toothbrush_homeassistant.py and https://esphome.io/components/esp32_ble_tracker.html for more information.
This commit is contained in:
Ian 2020-05-03 15:23:21 +01:00
parent 7fa5cab8e3
commit 90c3cb62b3
No known key found for this signature in database
GPG Key ID: AFC31A7A03B5007A
8 changed files with 192 additions and 1 deletions

View File

@ -9,7 +9,7 @@ from esphome.const import CONF_ID, ESP_PLATFORM_ESP32, CONF_INTERVAL, \
from esphome.core import coroutine from esphome.core import coroutine
ESP_PLATFORMS = [ESP_PLATFORM_ESP32] ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
AUTO_LOAD = ['xiaomi_ble', 'ruuvi_ble'] AUTO_LOAD = ['xiaomi_ble', 'ruuvi_ble', 'oralb_ble']
CONF_ESP32_BLE_ID = 'esp32_ble_id' CONF_ESP32_BLE_ID = 'esp32_ble_id'
CONF_SCAN_PARAMETERS = 'scan_parameters' CONF_SCAN_PARAMETERS = 'scan_parameters'

View File

@ -0,0 +1,18 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import esp32_ble_tracker
from esphome.const import CONF_ID
DEPENDENCIES = ['esp32_ble_tracker']
oralb_ble_ns = cg.esphome_ns.namespace('oralb_ble')
OralbListener = oralb_ble_ns.class_('OralbListener', esp32_ble_tracker.ESPBTDeviceListener)
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(OralbListener),
}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield esp32_ble_tracker.register_ble_device(var, config)

View File

@ -0,0 +1,48 @@
#include "oralb_ble.h"
#include "esphome/core/log.h"
#ifdef ARDUINO_ARCH_ESP32
namespace esphome {
namespace oralb_ble {
static const char *TAG = "oralb_ble";
bool parse_oralb_data_byte(const esp32_ble_tracker::adv_data_t &adv_data, OralbParseResult &result) {
result.state = adv_data[3];
return true;
}
optional<OralbParseResult> parse_oralb(const esp32_ble_tracker::ESPBTDevice &device) {
bool success = false;
OralbParseResult result{};
for (auto &it : device.get_manufacturer_datas()) {
bool is_oralb = it.uuid.contains(0xDC, 0x00);
if (!is_oralb)
continue;
if (parse_oralb_data_byte(it.data, result))
success = true;
}
if (!success)
return {};
return result;
}
bool OralbListener::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
auto res = parse_oralb(device);
if (!res.has_value())
return false;
ESP_LOGD(TAG, "Got OralB (%s):", device.address_str().c_str());
if (res->state.has_value()) {
ESP_LOGD(TAG, " State: %d", *res->state);
}
return true;
}
} // namespace oralb_ble
} // namespace esphome
#endif

View File

@ -0,0 +1,27 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#ifdef ARDUINO_ARCH_ESP32
namespace esphome {
namespace oralb_ble {
struct OralbParseResult {
optional<uint8_t> state;
};
bool parse_oralb_data_byte(uint8_t data_type, const uint8_t *data, uint8_t data_length, OralbParseResult &result);
optional<OralbParseResult> parse_oralb(const esp32_ble_tracker::ESPBTDevice &device);
class OralbListener : public esp32_ble_tracker::ESPBTDeviceListener {
public:
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
};
} // namespace oralb_ble
} // namespace esphome
#endif

View File

@ -0,0 +1,19 @@
#include "oralb_brush.h"
#include "esphome/core/log.h"
#ifdef ARDUINO_ARCH_ESP32
namespace esphome {
namespace oralb_brush {
static const char *TAG = "oralb_brush";
void OralbBrush::dump_config() {
ESP_LOGCONFIG(TAG, "OralbBrush");
LOG_SENSOR(" ", "State", this->state_);
}
} // namespace oralb_brush
} // namespace esphome
#endif

View File

@ -0,0 +1,43 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/oralb_ble/oralb_ble.h"
#ifdef ARDUINO_ARCH_ESP32
namespace esphome {
namespace oralb_brush {
class OralbBrush : public Component, public esp32_ble_tracker::ESPBTDeviceListener {
public:
void set_address(uint64_t address) { address_ = address; }
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override {
if (device.address_uint64() != this->address_)
return false;
auto res = oralb_ble::parse_oralb(device);
if (!res.has_value())
return false;
if (res->state.has_value() && this->state_ != nullptr)
this->state_->publish_state(*res->state);
return true;
}
void dump_config() override;
float get_setup_priority() const override { return setup_priority::DATA; }
void set_state(sensor::Sensor *state) { state_ = state; }
protected:
uint64_t address_;
sensor::Sensor *state_{nullptr};
};
} // namespace oralb_brush
} // namespace esphome
#endif

View File

@ -0,0 +1,36 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, esp32_ble_tracker
from esphome.const import CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \
CONF_PRESSURE, CONF_ACCELERATION, CONF_ACCELERATION_X, CONF_ACCELERATION_Y, \
CONF_ACCELERATION_Z, CONF_BATTERY_VOLTAGE, CONF_TX_POWER, \
CONF_MEASUREMENT_SEQUENCE_NUMBER, CONF_MOVEMENT_COUNTER, UNIT_CELSIUS, \
ICON_THERMOMETER, UNIT_PERCENT, UNIT_VOLT, UNIT_HECTOPASCAL, UNIT_G, \
UNIT_DECIBEL_MILLIWATT, UNIT_EMPTY, ICON_WATER_PERCENT, ICON_BATTERY, \
ICON_GAUGE, ICON_ACCELERATION, ICON_ACCELERATION_X, ICON_ACCELERATION_Y, \
ICON_ACCELERATION_Z, ICON_SIGNAL, CONF_ID, ICON_EMPTY, CONF_STATE
DEPENDENCIES = ['esp32_ble_tracker']
AUTO_LOAD = ['oralb_ble']
oralb_brush_ns = cg.esphome_ns.namespace('oralb_brush')
OralbBrush = oralb_brush_ns.class_(
'OralbBrush', esp32_ble_tracker.ESPBTDeviceListener, cg.Component)
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(OralbBrush),
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
cv.Optional(CONF_STATE): sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 0),
}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA)
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config)
yield esp32_ble_tracker.register_ble_device(var, config)
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
if CONF_STATE in config:
sens = yield sensor.new_sensor(config[CONF_STATE])
cg.add(var.set_state(sens))