mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	[Packet transport] Ping timeout sensor (#8694)
This commit is contained in:
		| @@ -1,19 +1,76 @@ | ||||
| import esphome.codegen as cg | ||||
| from esphome.components import binary_sensor | ||||
| from esphome.const import CONF_ID | ||||
| import esphome.config_validation as cv | ||||
| from esphome.const import ( | ||||
|     CONF_DATA, | ||||
|     CONF_ID, | ||||
|     CONF_NAME, | ||||
|     CONF_STATUS, | ||||
|     CONF_TYPE, | ||||
|     DEVICE_CLASS_CONNECTIVITY, | ||||
|     ENTITY_CATEGORY_DIAGNOSTIC, | ||||
| ) | ||||
| import esphome.final_validate as fv | ||||
|  | ||||
| from . import ( | ||||
|     CONF_ENCRYPTION, | ||||
|     CONF_PING_PONG_ENABLE, | ||||
|     CONF_PROVIDER, | ||||
|     CONF_PROVIDERS, | ||||
|     CONF_REMOTE_ID, | ||||
|     CONF_TRANSPORT_ID, | ||||
|     PacketTransport, | ||||
|     packet_transport_sensor_schema, | ||||
|     provider_name_validate, | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = packet_transport_sensor_schema(binary_sensor.binary_sensor_schema()) | ||||
| STATUS_SENSOR_SCHEMA = binary_sensor.binary_sensor_schema( | ||||
|     device_class=DEVICE_CLASS_CONNECTIVITY, | ||||
|     entity_category=ENTITY_CATEGORY_DIAGNOSTIC, | ||||
| ).extend( | ||||
|     { | ||||
|         cv.GenerateID(CONF_TRANSPORT_ID): cv.use_id(PacketTransport), | ||||
|         cv.Required(CONF_PROVIDER): provider_name_validate, | ||||
|     } | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.typed_schema( | ||||
|     { | ||||
|         CONF_DATA: packet_transport_sensor_schema(binary_sensor.binary_sensor_schema()), | ||||
|         CONF_STATUS: STATUS_SENSOR_SCHEMA, | ||||
|     }, | ||||
|     key=CONF_TYPE, | ||||
|     default_type=CONF_DATA, | ||||
| ) | ||||
|  | ||||
|  | ||||
| def _final_validate(config): | ||||
|     if config[CONF_TYPE] != CONF_STATUS: | ||||
|         # Only run this validation if a status sensor is being configured | ||||
|         return config | ||||
|     full_config = fv.full_config.get() | ||||
|     transport_path = full_config.get_path_for_id(config[CONF_TRANSPORT_ID])[:-1] | ||||
|     transport_config = full_config.get_config_for_path(transport_path) | ||||
|     if transport_config[CONF_PING_PONG_ENABLE] and any( | ||||
|         CONF_ENCRYPTION in p | ||||
|         for p in transport_config[CONF_PROVIDERS] | ||||
|         if p[CONF_NAME] == config[CONF_PROVIDER] | ||||
|     ): | ||||
|         return config | ||||
|     raise cv.Invalid( | ||||
|         "Status sensor requires ping-pong to be enabled and the nominated provider to use encryption." | ||||
|     ) | ||||
|  | ||||
|  | ||||
| FINAL_VALIDATE_SCHEMA = _final_validate | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     var = await binary_sensor.new_binary_sensor(config) | ||||
|     comp = await cg.get_variable(config[CONF_TRANSPORT_ID]) | ||||
|     remote_id = str(config.get(CONF_REMOTE_ID) or config.get(CONF_ID)) | ||||
|     cg.add(comp.add_remote_binary_sensor(config[CONF_PROVIDER], remote_id, var)) | ||||
|     if config[CONF_TYPE] == CONF_STATUS: | ||||
|         cg.add(comp.set_provider_status_sensor(config[CONF_PROVIDER], var)) | ||||
|         cg.add_define("USE_STATUS_SENSOR") | ||||
|     else:  # CONF_DATA is default | ||||
|         remote_id = str(config.get(CONF_REMOTE_ID) or config.get(CONF_ID)) | ||||
|         cg.add(comp.add_remote_binary_sensor(config[CONF_PROVIDER], remote_id, var)) | ||||
|   | ||||
| @@ -317,8 +317,37 @@ void PacketTransport::update() { | ||||
|   auto now = millis() / 1000; | ||||
|   if (this->last_key_time_ + this->ping_pong_recyle_time_ < now) { | ||||
|     this->resend_ping_key_ = this->ping_pong_enable_; | ||||
|     ESP_LOGV(TAG, "Ping request, age %u", now - this->last_key_time_); | ||||
|     this->last_key_time_ = now; | ||||
|   } | ||||
|   for (const auto &provider : this->providers_) { | ||||
|     uint32_t key_response_age = now - provider.second.last_key_response_time; | ||||
|     if (key_response_age > (this->ping_pong_recyle_time_ * 2u)) { | ||||
| #ifdef USE_STATUS_SENSOR | ||||
|       if (provider.second.status_sensor != nullptr && provider.second.status_sensor->state) { | ||||
|         ESP_LOGI(TAG, "Ping status for %s timeout at %u with age %u", provider.first.c_str(), now, key_response_age); | ||||
|         provider.second.status_sensor->publish_state(false); | ||||
|       } | ||||
| #endif | ||||
| #ifdef USE_SENSOR | ||||
|       for (auto &sensor : this->remote_sensors_[provider.first]) { | ||||
|         sensor.second->publish_state(NAN); | ||||
|       } | ||||
| #endif | ||||
| #ifdef USE_BINARY_SENSOR | ||||
|       for (auto &sensor : this->remote_binary_sensors_[provider.first]) { | ||||
|         sensor.second->invalidate_state(); | ||||
|       } | ||||
| #endif | ||||
|     } else { | ||||
| #ifdef USE_STATUS_SENSOR | ||||
|       if (provider.second.status_sensor != nullptr && !provider.second.status_sensor->state) { | ||||
|         ESP_LOGI(TAG, "Ping status for %s restored at %u with age %u", provider.first.c_str(), now, key_response_age); | ||||
|         provider.second.status_sensor->publish_state(true); | ||||
|       } | ||||
| #endif | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| void PacketTransport::add_key_(const char *name, uint32_t key) { | ||||
| @@ -437,7 +466,8 @@ void PacketTransport::process_(const std::vector<uint8_t> &data) { | ||||
|     if (decoder.decode(PING_KEY, key) == DECODE_OK) { | ||||
|       if (key == this->ping_key_) { | ||||
|         ping_key_seen = true; | ||||
|         ESP_LOGV(TAG, "Found good ping key %X", (unsigned) key); | ||||
|         provider.last_key_response_time = millis() / 1000; | ||||
|         ESP_LOGV(TAG, "Found good ping key %X at timestamp %" PRIu32, (unsigned) key, provider.last_key_response_time); | ||||
|       } else { | ||||
|         ESP_LOGV(TAG, "Unknown ping key %X", (unsigned) key); | ||||
|       } | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
| #ifdef USE_BINARY_SENSOR | ||||
| #include "esphome/components/binary_sensor/binary_sensor.h" | ||||
| #endif | ||||
| # | ||||
|  | ||||
| #include <vector> | ||||
| #include <map> | ||||
|  | ||||
| @@ -27,6 +27,10 @@ struct Provider { | ||||
|   std::vector<uint8_t> encryption_key; | ||||
|   const char *name; | ||||
|   uint32_t last_code[2]; | ||||
|   uint32_t last_key_response_time; | ||||
| #ifdef USE_STATUS_SENSOR | ||||
|   binary_sensor::BinarySensor *status_sensor{nullptr}; | ||||
| #endif | ||||
| }; | ||||
|  | ||||
| #ifdef USE_SENSOR | ||||
| @@ -75,10 +79,7 @@ class PacketTransport : public PollingComponent { | ||||
|  | ||||
|   void add_provider(const char *hostname) { | ||||
|     if (this->providers_.count(hostname) == 0) { | ||||
|       Provider provider; | ||||
|       provider.encryption_key = std::vector<uint8_t>{}; | ||||
|       provider.last_code[0] = 0; | ||||
|       provider.last_code[1] = 0; | ||||
|       Provider provider{}; | ||||
|       provider.name = hostname; | ||||
|       this->providers_[hostname] = provider; | ||||
| #ifdef USE_SENSOR | ||||
| @@ -97,6 +98,11 @@ class PacketTransport : public PollingComponent { | ||||
|   void set_provider_encryption(const char *name, std::vector<uint8_t> key) { | ||||
|     this->providers_[name].encryption_key = std::move(key); | ||||
|   } | ||||
| #ifdef USE_STATUS_SENSOR | ||||
|   void set_provider_status_sensor(const char *name, binary_sensor::BinarySensor *sensor) { | ||||
|     this->providers_[name].status_sensor = sensor; | ||||
|   } | ||||
| #endif | ||||
|   void set_platform_name(const char *name) { this->platform_name_ = name; } | ||||
|  | ||||
|  protected: | ||||
|   | ||||
| @@ -86,6 +86,7 @@ | ||||
| #define USE_SELECT | ||||
| #define USE_SENSOR | ||||
| #define USE_STATUS_LED | ||||
| #define USE_STATUS_SENSOR | ||||
| #define USE_SWITCH | ||||
| #define USE_TEXT | ||||
| #define USE_TEXT_SENSOR | ||||
|   | ||||
		Reference in New Issue
	
	Block a user