mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +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) { | ||||
|   BLEClientBase::set_state(state); | ||||
|   for (auto &node : nodes_) | ||||
|   | ||||
| @@ -27,7 +27,8 @@ class BLEClientNode { | ||||
|  public: | ||||
|   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; | ||||
|   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; } | ||||
|   espbt::ESPBTClient *client; | ||||
|   // 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, | ||||
|                            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; | ||||
|  | ||||
|   void set_enabled(bool enabled); | ||||
|   | ||||
| @@ -5,7 +5,11 @@ from esphome.const import ( | ||||
|     CONF_CHARACTERISTIC_UUID, | ||||
|     CONF_LAMBDA, | ||||
|     CONF_TRIGGER_ID, | ||||
|     CONF_TYPE, | ||||
|     CONF_SERVICE_UUID, | ||||
|     DEVICE_CLASS_SIGNAL_STRENGTH, | ||||
|     STATE_CLASS_MEASUREMENT, | ||||
|     UNIT_DECIBEL_MILLIWATT, | ||||
| ) | ||||
| from esphome import automation | ||||
| from .. import ble_client_ns | ||||
| @@ -16,6 +20,8 @@ CONF_DESCRIPTOR_UUID = "descriptor_uuid" | ||||
|  | ||||
| CONF_NOTIFY = "notify" | ||||
| CONF_ON_NOTIFY = "on_notify" | ||||
| TYPE_CHARACTERISTIC = "characteristic" | ||||
| TYPE_RSSI = "rssi" | ||||
|  | ||||
| adv_data_t = cg.std_vector.template(cg.uint8) | ||||
| 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_) | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.All( | ||||
|     sensor.sensor_schema( | ||||
|         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) | ||||
| BLEClientRssiSensor = ble_client_ns.class_( | ||||
|     "BLEClientRSSISensor", sensor.Sensor, cg.PollingComponent, ble_client.BLEClientNode | ||||
| ) | ||||
|  | ||||
|  | ||||
| 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) | ||||
|     if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format): | ||||
|         cg.add( | ||||
| @@ -125,3 +165,10 @@ async def to_code(config): | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||
|         await ble_client.register_ble_node(trigger, config) | ||||
|         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: | ||||
|   - platform: ble_client | ||||
|     type: characteristic | ||||
|     ble_client_id: ble_foo | ||||
|     name: Green iTag btn | ||||
|     service_uuid: ffe0 | ||||
| @@ -335,6 +336,11 @@ sensor: | ||||
|       then: | ||||
|         - lambda: |- | ||||
|             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 | ||||
|     pin: A0 | ||||
|     name: Living Room Brightness | ||||
|   | ||||
		Reference in New Issue
	
	Block a user