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:
parent
7fa5cab8e3
commit
90c3cb62b3
@ -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'
|
||||||
|
18
esphome/components/oralb_ble/__init__.py
Normal file
18
esphome/components/oralb_ble/__init__.py
Normal 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)
|
48
esphome/components/oralb_ble/oralb_ble.cpp
Normal file
48
esphome/components/oralb_ble/oralb_ble.cpp
Normal 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
|
27
esphome/components/oralb_ble/oralb_ble.h
Normal file
27
esphome/components/oralb_ble/oralb_ble.h
Normal 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
|
0
esphome/components/oralb_brush/__init__.py
Normal file
0
esphome/components/oralb_brush/__init__.py
Normal file
19
esphome/components/oralb_brush/oralb_brush.cpp
Normal file
19
esphome/components/oralb_brush/oralb_brush.cpp
Normal 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
|
43
esphome/components/oralb_brush/oralb_brush.h
Normal file
43
esphome/components/oralb_brush/oralb_brush.h
Normal 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
|
36
esphome/components/oralb_brush/sensor.py
Normal file
36
esphome/components/oralb_brush/sensor.py
Normal 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))
|
Loading…
x
Reference in New Issue
Block a user