mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	Add ble RSSI sensor for connected devices (#3860)
This commit is contained in:
		| @@ -64,6 +64,13 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void BLEClient::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { | ||||||
|  |   BLEClientBase::gap_event_handler(event, param); | ||||||
|  |  | ||||||
|  |   for (auto *node : this->nodes_) | ||||||
|  |     node->gap_event_handler(event, param); | ||||||
|  | } | ||||||
|  |  | ||||||
| void BLEClient::set_state(espbt::ClientState state) { | void BLEClient::set_state(espbt::ClientState state) { | ||||||
|   BLEClientBase::set_state(state); |   BLEClientBase::set_state(state); | ||||||
|   for (auto &node : nodes_) |   for (auto &node : nodes_) | ||||||
|   | |||||||
| @@ -27,7 +27,8 @@ class BLEClientNode { | |||||||
|  public: |  public: | ||||||
|   virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, |   virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, | ||||||
|                                    esp_ble_gattc_cb_param_t *param) = 0; |                                    esp_ble_gattc_cb_param_t *param) = 0; | ||||||
|   virtual void loop(){}; |   virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {} | ||||||
|  |   virtual void loop() {} | ||||||
|   void set_address(uint64_t address) { address_ = address; } |   void set_address(uint64_t address) { address_ = address; } | ||||||
|   espbt::ESPBTClient *client; |   espbt::ESPBTClient *client; | ||||||
|   // This should be transitioned to Established once the node no longer needs |   // This should be transitioned to Established once the node no longer needs | ||||||
| @@ -51,6 +52,8 @@ class BLEClient : public BLEClientBase { | |||||||
|  |  | ||||||
|   void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, |   void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, | ||||||
|                            esp_ble_gattc_cb_param_t *param) override; |                            esp_ble_gattc_cb_param_t *param) override; | ||||||
|  |  | ||||||
|  |   void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override; | ||||||
|   bool parse_device(const espbt::ESPBTDevice &device) override; |   bool parse_device(const espbt::ESPBTDevice &device) override; | ||||||
|  |  | ||||||
|   void set_enabled(bool enabled); |   void set_enabled(bool enabled); | ||||||
|   | |||||||
| @@ -5,7 +5,11 @@ from esphome.const import ( | |||||||
|     CONF_CHARACTERISTIC_UUID, |     CONF_CHARACTERISTIC_UUID, | ||||||
|     CONF_LAMBDA, |     CONF_LAMBDA, | ||||||
|     CONF_TRIGGER_ID, |     CONF_TRIGGER_ID, | ||||||
|  |     CONF_TYPE, | ||||||
|     CONF_SERVICE_UUID, |     CONF_SERVICE_UUID, | ||||||
|  |     DEVICE_CLASS_SIGNAL_STRENGTH, | ||||||
|  |     STATE_CLASS_MEASUREMENT, | ||||||
|  |     UNIT_DECIBEL_MILLIWATT, | ||||||
| ) | ) | ||||||
| from esphome import automation | from esphome import automation | ||||||
| from .. import ble_client_ns | from .. import ble_client_ns | ||||||
| @@ -16,6 +20,8 @@ CONF_DESCRIPTOR_UUID = "descriptor_uuid" | |||||||
|  |  | ||||||
| CONF_NOTIFY = "notify" | CONF_NOTIFY = "notify" | ||||||
| CONF_ON_NOTIFY = "on_notify" | CONF_ON_NOTIFY = "on_notify" | ||||||
|  | TYPE_CHARACTERISTIC = "characteristic" | ||||||
|  | TYPE_RSSI = "rssi" | ||||||
|  |  | ||||||
| adv_data_t = cg.std_vector.template(cg.uint8) | adv_data_t = cg.std_vector.template(cg.uint8) | ||||||
| adv_data_t_const_ref = adv_data_t.operator("ref").operator("const") | adv_data_t_const_ref = adv_data_t.operator("ref").operator("const") | ||||||
| @@ -27,33 +33,67 @@ BLESensorNotifyTrigger = ble_client_ns.class_( | |||||||
|     "BLESensorNotifyTrigger", automation.Trigger.template(cg.float_) |     "BLESensorNotifyTrigger", automation.Trigger.template(cg.float_) | ||||||
| ) | ) | ||||||
|  |  | ||||||
| CONFIG_SCHEMA = cv.All( | BLEClientRssiSensor = ble_client_ns.class_( | ||||||
|     sensor.sensor_schema( |     "BLEClientRSSISensor", sensor.Sensor, cg.PollingComponent, ble_client.BLEClientNode | ||||||
|         BLESensor, |  | ||||||
|         accuracy_decimals=0, |  | ||||||
|     ) |  | ||||||
|     .extend( |  | ||||||
|         { |  | ||||||
|             cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, |  | ||||||
|             cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid, |  | ||||||
|             cv.Optional(CONF_DESCRIPTOR_UUID): esp32_ble_tracker.bt_uuid, |  | ||||||
|             cv.Optional(CONF_LAMBDA): cv.returning_lambda, |  | ||||||
|             cv.Optional(CONF_NOTIFY, default=False): cv.boolean, |  | ||||||
|             cv.Optional(CONF_ON_NOTIFY): automation.validate_automation( |  | ||||||
|                 { |  | ||||||
|                     cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( |  | ||||||
|                         BLESensorNotifyTrigger |  | ||||||
|                     ), |  | ||||||
|                 } |  | ||||||
|             ), |  | ||||||
|         } |  | ||||||
|     ) |  | ||||||
|     .extend(cv.polling_component_schema("60s")) |  | ||||||
|     .extend(ble_client.BLE_CLIENT_SCHEMA) |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| async def to_code(config): | def checkType(value): | ||||||
|  |     if CONF_TYPE not in value and CONF_SERVICE_UUID in value: | ||||||
|  |         raise cv.Invalid( | ||||||
|  |             "Looks like you're trying to create a ble characteristic sensor. Please add `type: characteristic` to your sensor config." | ||||||
|  |         ) | ||||||
|  |     return value | ||||||
|  |  | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = cv.All( | ||||||
|  |     checkType, | ||||||
|  |     cv.typed_schema( | ||||||
|  |         { | ||||||
|  |             TYPE_CHARACTERISTIC: sensor.sensor_schema( | ||||||
|  |                 BLESensor, | ||||||
|  |                 accuracy_decimals=0, | ||||||
|  |             ) | ||||||
|  |             .extend(cv.polling_component_schema("60s")) | ||||||
|  |             .extend(ble_client.BLE_CLIENT_SCHEMA) | ||||||
|  |             .extend( | ||||||
|  |                 { | ||||||
|  |                     cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, | ||||||
|  |                     cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid, | ||||||
|  |                     cv.Optional(CONF_DESCRIPTOR_UUID): esp32_ble_tracker.bt_uuid, | ||||||
|  |                     cv.Optional(CONF_LAMBDA): cv.returning_lambda, | ||||||
|  |                     cv.Optional(CONF_NOTIFY, default=False): cv.boolean, | ||||||
|  |                     cv.Optional(CONF_ON_NOTIFY): automation.validate_automation( | ||||||
|  |                         { | ||||||
|  |                             cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( | ||||||
|  |                                 BLESensorNotifyTrigger | ||||||
|  |                             ), | ||||||
|  |                         } | ||||||
|  |                     ), | ||||||
|  |                 } | ||||||
|  |             ), | ||||||
|  |             TYPE_RSSI: sensor.sensor_schema( | ||||||
|  |                 BLEClientRssiSensor, | ||||||
|  |                 accuracy_decimals=0, | ||||||
|  |                 unit_of_measurement=UNIT_DECIBEL_MILLIWATT, | ||||||
|  |                 device_class=DEVICE_CLASS_SIGNAL_STRENGTH, | ||||||
|  |                 state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |             ) | ||||||
|  |             .extend(cv.polling_component_schema("60s")) | ||||||
|  |             .extend(ble_client.BLE_CLIENT_SCHEMA), | ||||||
|  |         }, | ||||||
|  |         lower=True, | ||||||
|  |     ), | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def rssi_sensor_to_code(config): | ||||||
|  |     var = await sensor.new_sensor(config) | ||||||
|  |     await cg.register_component(var, config) | ||||||
|  |     await ble_client.register_ble_node(var, config) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def characteristic_sensor_to_code(config): | ||||||
|     var = await sensor.new_sensor(config) |     var = await sensor.new_sensor(config) | ||||||
|     if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format): |     if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format): | ||||||
|         cg.add( |         cg.add( | ||||||
| @@ -125,3 +165,10 @@ async def to_code(config): | |||||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) |         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||||
|         await ble_client.register_ble_node(trigger, config) |         await ble_client.register_ble_node(trigger, config) | ||||||
|         await automation.build_automation(trigger, [(float, "x")], conf) |         await automation.build_automation(trigger, [(float, "x")], conf) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     if config[CONF_TYPE] == TYPE_RSSI: | ||||||
|  |         await rssi_sensor_to_code(config) | ||||||
|  |     elif config[CONF_TYPE] == TYPE_CHARACTERISTIC: | ||||||
|  |         await characteristic_sensor_to_code(config) | ||||||
|   | |||||||
							
								
								
									
										78
									
								
								esphome/components/ble_client/sensor/ble_rssi_sensor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								esphome/components/ble_client/sensor/ble_rssi_sensor.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | |||||||
|  | #include "ble_rssi_sensor.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  | #include "esphome/core/application.h" | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  | #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" | ||||||
|  |  | ||||||
|  | #ifdef USE_ESP32 | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ble_client { | ||||||
|  |  | ||||||
|  | static const char *const TAG = "ble_rssi_sensor"; | ||||||
|  |  | ||||||
|  | void BLEClientRSSISensor::loop() {} | ||||||
|  |  | ||||||
|  | void BLEClientRSSISensor::dump_config() { | ||||||
|  |   LOG_SENSOR("", "BLE Client RSSI Sensor", this); | ||||||
|  |   ESP_LOGCONFIG(TAG, "  MAC address        : %s", this->parent()->address_str().c_str()); | ||||||
|  |   LOG_UPDATE_INTERVAL(this); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void BLEClientRSSISensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, | ||||||
|  |                                               esp_ble_gattc_cb_param_t *param) { | ||||||
|  |   switch (event) { | ||||||
|  |     case ESP_GATTC_OPEN_EVT: { | ||||||
|  |       if (param->open.status == ESP_GATT_OK) { | ||||||
|  |         ESP_LOGI(TAG, "[%s] Connected successfully!", this->get_name().c_str()); | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     case ESP_GATTC_DISCONNECT_EVT: { | ||||||
|  |       ESP_LOGW(TAG, "[%s] Disconnected!", this->get_name().c_str()); | ||||||
|  |       this->status_set_warning(); | ||||||
|  |       this->publish_state(NAN); | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     case ESP_GATTC_SEARCH_CMPL_EVT: | ||||||
|  |       this->node_state = espbt::ClientState::ESTABLISHED; | ||||||
|  |       break; | ||||||
|  |     default: | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void BLEClientRSSISensor::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { | ||||||
|  |   switch (event) { | ||||||
|  |     // server response on RSSI request: | ||||||
|  |     case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: | ||||||
|  |       if (param->read_rssi_cmpl.status == ESP_BT_STATUS_SUCCESS) { | ||||||
|  |         int8_t rssi = param->read_rssi_cmpl.rssi; | ||||||
|  |         ESP_LOGI(TAG, "ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT RSSI: %d", rssi); | ||||||
|  |         this->publish_state(rssi); | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  |     default: | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void BLEClientRSSISensor::update() { | ||||||
|  |   if (this->node_state != espbt::ClientState::ESTABLISHED) { | ||||||
|  |     ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->get_name().c_str()); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   ESP_LOGV(TAG, "requesting rssi from %s", this->parent()->address_str().c_str()); | ||||||
|  |   auto status = esp_ble_gap_read_rssi(this->parent()->get_remote_bda()); | ||||||
|  |   if (status != ESP_OK) { | ||||||
|  |     ESP_LOGW(TAG, "esp_ble_gap_read_rssi error, address=%s, status=%d", this->parent()->address_str().c_str(), status); | ||||||
|  |     this->status_set_warning(); | ||||||
|  |     this->publish_state(NAN); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace ble_client | ||||||
|  | }  // namespace esphome | ||||||
|  | #endif | ||||||
							
								
								
									
										31
									
								
								esphome/components/ble_client/sensor/ble_rssi_sensor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								esphome/components/ble_client/sensor/ble_rssi_sensor.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/components/ble_client/ble_client.h" | ||||||
|  | #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" | ||||||
|  | #include "esphome/components/sensor/sensor.h" | ||||||
|  |  | ||||||
|  | #ifdef USE_ESP32 | ||||||
|  | #include <esp_gattc_api.h> | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ble_client { | ||||||
|  |  | ||||||
|  | namespace espbt = esphome::esp32_ble_tracker; | ||||||
|  |  | ||||||
|  | class BLEClientRSSISensor : public sensor::Sensor, public PollingComponent, public BLEClientNode { | ||||||
|  |  public: | ||||||
|  |   void loop() override; | ||||||
|  |   void update() override; | ||||||
|  |   void dump_config() override; | ||||||
|  |   float get_setup_priority() const override { return setup_priority::DATA; } | ||||||
|  |  | ||||||
|  |   void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override; | ||||||
|  |  | ||||||
|  |   void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, | ||||||
|  |                            esp_ble_gattc_cb_param_t *param) override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ble_client | ||||||
|  | }  // namespace esphome | ||||||
|  | #endif | ||||||
| @@ -321,6 +321,7 @@ mcp23s17: | |||||||
|  |  | ||||||
| sensor: | sensor: | ||||||
|   - platform: ble_client |   - platform: ble_client | ||||||
|  |     type: characteristic | ||||||
|     ble_client_id: ble_foo |     ble_client_id: ble_foo | ||||||
|     name: Green iTag btn |     name: Green iTag btn | ||||||
|     service_uuid: ffe0 |     service_uuid: ffe0 | ||||||
| @@ -335,6 +336,11 @@ sensor: | |||||||
|       then: |       then: | ||||||
|         - lambda: |- |         - lambda: |- | ||||||
|             ESP_LOGD("green_btn", "Button was pressed, val%f", x); |             ESP_LOGD("green_btn", "Button was pressed, val%f", x); | ||||||
|  |   - platform: ble_client | ||||||
|  |     type: rssi | ||||||
|  |     ble_client_id: ble_foo | ||||||
|  |     name: Green iTag RSSI | ||||||
|  |     update_interval: 15s | ||||||
|   - platform: adc |   - platform: adc | ||||||
|     pin: A0 |     pin: A0 | ||||||
|     name: Living Room Brightness |     name: Living Room Brightness | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user