forked from esphome/esphome
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
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.- Loading branch information
Ian
committed
May 3, 2020
1 parent
7fa5cab
commit 90c3cb6
Showing
8 changed files
with
192 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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)) |
90c3cb6There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello, this is awesome work, Could you please open a PR against the main esphome?
It would make it available to newcomers to the project. Many thanks for considering.
90c3cb6There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd love to also see this!
90c3cb6There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Second this.
Failing that, what's the cleanest way to get this patch applied to the upstream version? Installing from this cloned repository instead doesn't seem like a good idea.
90c3cb6There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you still working on this?
I would love to see this as an HA integration.
I can imagine things like:
90c3cb6There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes in
esphome/components/esp32_ble_tracker/__init__.pyare not needed anymore (see esphome#2617), the integration can be loaded in the esp yaml:# Example config.yamlesp32_ble_tracker:oralb-ble:The documentation says: "Just clone the repository locally, do the changes for your new feature/bug fix and submit a pull request."
In the ESP forum there is already a feature request (see esphome/feature-requests#1062) for the integration, however the procrastination level is comparable. I will continue in esphome/feature-requests#1062 with the next steps clone, change, test etc. I have a couple of ESPs and OralBs laying around and will try to get it working.
Any help from @ehn @MilanGajicBuva @KiLLeRRaT @marji is appreciated :-)