mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	Merge branch 'sensor_memory' into integration
This commit is contained in:
		| @@ -148,6 +148,7 @@ BinarySensorCondition = binary_sensor_ns.class_("BinarySensorCondition", Conditi | |||||||
|  |  | ||||||
| # Filters | # Filters | ||||||
| Filter = binary_sensor_ns.class_("Filter") | Filter = binary_sensor_ns.class_("Filter") | ||||||
|  | TimeoutFilter = binary_sensor_ns.class_("TimeoutFilter", Filter, cg.Component) | ||||||
| DelayedOnOffFilter = binary_sensor_ns.class_("DelayedOnOffFilter", Filter, cg.Component) | DelayedOnOffFilter = binary_sensor_ns.class_("DelayedOnOffFilter", Filter, cg.Component) | ||||||
| DelayedOnFilter = binary_sensor_ns.class_("DelayedOnFilter", Filter, cg.Component) | DelayedOnFilter = binary_sensor_ns.class_("DelayedOnFilter", Filter, cg.Component) | ||||||
| DelayedOffFilter = binary_sensor_ns.class_("DelayedOffFilter", Filter, cg.Component) | DelayedOffFilter = binary_sensor_ns.class_("DelayedOffFilter", Filter, cg.Component) | ||||||
| @@ -171,6 +172,19 @@ async def invert_filter_to_code(config, filter_id): | |||||||
|     return cg.new_Pvariable(filter_id) |     return cg.new_Pvariable(filter_id) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @register_filter( | ||||||
|  |     "timeout", | ||||||
|  |     TimeoutFilter, | ||||||
|  |     cv.templatable(cv.positive_time_period_milliseconds), | ||||||
|  | ) | ||||||
|  | async def timeout_filter_to_code(config, filter_id): | ||||||
|  |     var = cg.new_Pvariable(filter_id) | ||||||
|  |     await cg.register_component(var, {}) | ||||||
|  |     template_ = await cg.templatable(config, [], cg.uint32) | ||||||
|  |     cg.add(var.set_timeout_value(template_)) | ||||||
|  |     return var | ||||||
|  |  | ||||||
|  |  | ||||||
| @register_filter( | @register_filter( | ||||||
|     "delayed_on_off", |     "delayed_on_off", | ||||||
|     DelayedOnOffFilter, |     DelayedOnOffFilter, | ||||||
|   | |||||||
| @@ -25,6 +25,12 @@ void Filter::input(bool value) { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void TimeoutFilter::input(bool value) { | ||||||
|  |   this->set_timeout("timeout", this->timeout_delay_.value(), [this]() { this->parent_->invalidate_state(); }); | ||||||
|  |   // we do not de-dup here otherwise changes from invalid to valid state will not be output | ||||||
|  |   this->output(value); | ||||||
|  | } | ||||||
|  |  | ||||||
| optional<bool> DelayedOnOffFilter::new_value(bool value) { | optional<bool> DelayedOnOffFilter::new_value(bool value) { | ||||||
|   if (value) { |   if (value) { | ||||||
|     this->set_timeout("ON_OFF", this->on_delay_.value(), [this]() { this->output(true); }); |     this->set_timeout("ON_OFF", this->on_delay_.value(), [this]() { this->output(true); }); | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ class Filter { | |||||||
|  public: |  public: | ||||||
|   virtual optional<bool> new_value(bool value) = 0; |   virtual optional<bool> new_value(bool value) = 0; | ||||||
|  |  | ||||||
|   void input(bool value); |   virtual void input(bool value); | ||||||
|  |  | ||||||
|   void output(bool value); |   void output(bool value); | ||||||
|  |  | ||||||
| @@ -28,6 +28,16 @@ class Filter { | |||||||
|   Deduplicator<bool> dedup_; |   Deduplicator<bool> dedup_; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | class TimeoutFilter : public Filter, public Component { | ||||||
|  |  public: | ||||||
|  |   optional<bool> new_value(bool value) override { return value; } | ||||||
|  |   void input(bool value) override; | ||||||
|  |   template<typename T> void set_timeout_value(T timeout) { this->timeout_delay_ = timeout; } | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   TemplatableValue<uint32_t> timeout_delay_{}; | ||||||
|  | }; | ||||||
|  |  | ||||||
| class DelayedOnOffFilter : public Filter, public Component { | class DelayedOnOffFilter : public Filter, public Component { | ||||||
|  public: |  public: | ||||||
|   optional<bool> new_value(bool value) override; |   optional<bool> new_value(bool value) override; | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ | |||||||
| #include "ble_event_pool.h" | #include "ble_event_pool.h" | ||||||
|  |  | ||||||
| #include "esphome/core/application.h" | #include "esphome/core/application.h" | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
| #include "esphome/core/log.h" | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
| #include <esp_bt.h> | #include <esp_bt.h> | ||||||
| @@ -516,13 +517,12 @@ void ESP32BLE::dump_config() { | |||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     ESP_LOGCONFIG(TAG, |     ESP_LOGCONFIG(TAG, | ||||||
|                   "ESP32 BLE:\n" |                   "BLE:\n" | ||||||
|                   "  MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n" |                   "  MAC address: %s\n" | ||||||
|                   "  IO Capability: %s", |                   "  IO Capability: %s", | ||||||
|                   mac_address[0], mac_address[1], mac_address[2], mac_address[3], mac_address[4], mac_address[5], |                   format_mac_address_pretty(mac_address).c_str(), io_capability_s); | ||||||
|                   io_capability_s); |  | ||||||
|   } else { |   } else { | ||||||
|     ESP_LOGCONFIG(TAG, "ESP32 BLE: bluetooth stack is not enabled"); |     ESP_LOGCONFIG(TAG, "Bluetooth stack is not enabled"); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										0
									
								
								esphome/components/esp32_hall/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/esp32_hall/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										5
									
								
								esphome/components/esp32_hall/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								esphome/components/esp32_hall/sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | import esphome.config_validation as cv | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = cv.invalid( | ||||||
|  |     "The esp32_hall component has been removed as of ESPHome 2025.7.0. See https://github.com/esphome/esphome/pull/9117 for details." | ||||||
|  | ) | ||||||
| @@ -8,6 +8,8 @@ | |||||||
| #include "esphome/components/sensor/sensor.h" | #include "esphome/components/sensor/sensor.h" | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | #include "esphome/core/application.h" | ||||||
|  |  | ||||||
| #define highbyte(val) (uint8_t)((val) >> 8) | #define highbyte(val) (uint8_t)((val) >> 8) | ||||||
| #define lowbyte(val) (uint8_t)((val) &0xff) | #define lowbyte(val) (uint8_t)((val) &0xff) | ||||||
|  |  | ||||||
| @@ -73,9 +75,9 @@ void LD2410Component::dump_config() { | |||||||
| #endif | #endif | ||||||
|   this->read_all_info(); |   this->read_all_info(); | ||||||
|   ESP_LOGCONFIG(TAG, |   ESP_LOGCONFIG(TAG, | ||||||
|                 "  Throttle_ : %ums\n" |                 "  Throttle: %ums\n" | ||||||
|                 "  MAC Address : %s\n" |                 "  MAC address: %s\n" | ||||||
|                 "  Firmware Version : %s", |                 "  Firmware version: %s", | ||||||
|                 this->throttle_, const_cast<char *>(this->mac_.c_str()), const_cast<char *>(this->version_.c_str())); |                 this->throttle_, const_cast<char *>(this->mac_.c_str()), const_cast<char *>(this->version_.c_str())); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -153,7 +155,7 @@ void LD2410Component::handle_periodic_data_(uint8_t *buffer, int len) { | |||||||
|   /* |   /* | ||||||
|     Reduce data update rate to prevent home assistant database size grow fast |     Reduce data update rate to prevent home assistant database size grow fast | ||||||
|   */ |   */ | ||||||
|   int32_t current_millis = millis(); |   int32_t current_millis = App.get_loop_component_start_time(); | ||||||
|   if (current_millis - last_periodic_millis_ < this->throttle_) |   if (current_millis - last_periodic_millis_ < this->throttle_) | ||||||
|     return; |     return; | ||||||
|   last_periodic_millis_ = current_millis; |   last_periodic_millis_ = current_millis; | ||||||
| @@ -299,21 +301,6 @@ const char MAC_FMT[] = "%02X:%02X:%02X:%02X:%02X:%02X"; | |||||||
| const std::string UNKNOWN_MAC("unknown"); | const std::string UNKNOWN_MAC("unknown"); | ||||||
| const std::string NO_MAC("08:05:04:03:02:01"); | const std::string NO_MAC("08:05:04:03:02:01"); | ||||||
|  |  | ||||||
| std::string format_mac(uint8_t *buffer) { |  | ||||||
|   std::string::size_type mac_size = 256; |  | ||||||
|   std::string mac; |  | ||||||
|   do { |  | ||||||
|     mac.resize(mac_size + 1); |  | ||||||
|     mac_size = std::snprintf(&mac[0], mac.size(), MAC_FMT, buffer[10], buffer[11], buffer[12], buffer[13], buffer[14], |  | ||||||
|                              buffer[15]); |  | ||||||
|   } while (mac_size + 1 > mac.size()); |  | ||||||
|   mac.resize(mac_size); |  | ||||||
|   if (mac == NO_MAC) { |  | ||||||
|     return UNKNOWN_MAC; |  | ||||||
|   } |  | ||||||
|   return mac; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #ifdef USE_NUMBER | #ifdef USE_NUMBER | ||||||
| std::function<void(void)> set_number_value(number::Number *n, float value) { | std::function<void(void)> set_number_value(number::Number *n, float value) { | ||||||
|   float normalized_value = value * 1.0; |   float normalized_value = value * 1.0; | ||||||
| @@ -328,40 +315,40 @@ std::function<void(void)> set_number_value(number::Number *n, float value) { | |||||||
| bool LD2410Component::handle_ack_data_(uint8_t *buffer, int len) { | bool LD2410Component::handle_ack_data_(uint8_t *buffer, int len) { | ||||||
|   ESP_LOGV(TAG, "Handling ACK DATA for COMMAND %02X", buffer[COMMAND]); |   ESP_LOGV(TAG, "Handling ACK DATA for COMMAND %02X", buffer[COMMAND]); | ||||||
|   if (len < 10) { |   if (len < 10) { | ||||||
|     ESP_LOGE(TAG, "Error with last command : incorrect length"); |     ESP_LOGE(TAG, "Invalid length"); | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|   if (buffer[0] != 0xFD || buffer[1] != 0xFC || buffer[2] != 0xFB || buffer[3] != 0xFA) {  // check 4 frame start bytes |   if (buffer[0] != 0xFD || buffer[1] != 0xFC || buffer[2] != 0xFB || buffer[3] != 0xFA) {  // check 4 frame start bytes | ||||||
|     ESP_LOGE(TAG, "Error with last command : incorrect Header"); |     ESP_LOGE(TAG, "Invalid header"); | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|   if (buffer[COMMAND_STATUS] != 0x01) { |   if (buffer[COMMAND_STATUS] != 0x01) { | ||||||
|     ESP_LOGE(TAG, "Error with last command : status != 0x01"); |     ESP_LOGE(TAG, "Invalid status"); | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|   if (this->two_byte_to_int_(buffer[8], buffer[9]) != 0x00) { |   if (this->two_byte_to_int_(buffer[8], buffer[9]) != 0x00) { | ||||||
|     ESP_LOGE(TAG, "Error with last command , last buffer was: %u , %u", buffer[8], buffer[9]); |     ESP_LOGE(TAG, "Invalid command: %u, %u", buffer[8], buffer[9]); | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   switch (buffer[COMMAND]) { |   switch (buffer[COMMAND]) { | ||||||
|     case lowbyte(CMD_ENABLE_CONF): |     case lowbyte(CMD_ENABLE_CONF): | ||||||
|       ESP_LOGV(TAG, "Handled Enable conf command"); |       ESP_LOGV(TAG, "Enable conf"); | ||||||
|       break; |       break; | ||||||
|     case lowbyte(CMD_DISABLE_CONF): |     case lowbyte(CMD_DISABLE_CONF): | ||||||
|       ESP_LOGV(TAG, "Handled Disabled conf command"); |       ESP_LOGV(TAG, "Disabled conf"); | ||||||
|       break; |       break; | ||||||
|     case lowbyte(CMD_SET_BAUD_RATE): |     case lowbyte(CMD_SET_BAUD_RATE): | ||||||
|       ESP_LOGV(TAG, "Handled baud rate change command"); |       ESP_LOGV(TAG, "Baud rate change"); | ||||||
| #ifdef USE_SELECT | #ifdef USE_SELECT | ||||||
|       if (this->baud_rate_select_ != nullptr) { |       if (this->baud_rate_select_ != nullptr) { | ||||||
|         ESP_LOGE(TAG, "Change baud rate component config to %s and reinstall", this->baud_rate_select_->state.c_str()); |         ESP_LOGE(TAG, "Configure baud rate to %s and reinstall", this->baud_rate_select_->state.c_str()); | ||||||
|       } |       } | ||||||
| #endif | #endif | ||||||
|       break; |       break; | ||||||
|     case lowbyte(CMD_VERSION): |     case lowbyte(CMD_VERSION): | ||||||
|       this->version_ = format_version(buffer); |       this->version_ = format_version(buffer); | ||||||
|       ESP_LOGV(TAG, "FW Version is: %s", const_cast<char *>(this->version_.c_str())); |       ESP_LOGV(TAG, "Firmware version: %s", const_cast<char *>(this->version_.c_str())); | ||||||
| #ifdef USE_TEXT_SENSOR | #ifdef USE_TEXT_SENSOR | ||||||
|       if (this->version_text_sensor_ != nullptr) { |       if (this->version_text_sensor_ != nullptr) { | ||||||
|         this->version_text_sensor_->publish_state(this->version_); |         this->version_text_sensor_->publish_state(this->version_); | ||||||
| @@ -371,7 +358,7 @@ bool LD2410Component::handle_ack_data_(uint8_t *buffer, int len) { | |||||||
|     case lowbyte(CMD_QUERY_DISTANCE_RESOLUTION): { |     case lowbyte(CMD_QUERY_DISTANCE_RESOLUTION): { | ||||||
|       std::string distance_resolution = |       std::string distance_resolution = | ||||||
|           DISTANCE_RESOLUTION_INT_TO_ENUM.at(this->two_byte_to_int_(buffer[10], buffer[11])); |           DISTANCE_RESOLUTION_INT_TO_ENUM.at(this->two_byte_to_int_(buffer[10], buffer[11])); | ||||||
|       ESP_LOGV(TAG, "Distance resolution is: %s", const_cast<char *>(distance_resolution.c_str())); |       ESP_LOGV(TAG, "Distance resolution: %s", const_cast<char *>(distance_resolution.c_str())); | ||||||
| #ifdef USE_SELECT | #ifdef USE_SELECT | ||||||
|       if (this->distance_resolution_select_ != nullptr && |       if (this->distance_resolution_select_ != nullptr && | ||||||
|           this->distance_resolution_select_->state != distance_resolution) { |           this->distance_resolution_select_->state != distance_resolution) { | ||||||
| @@ -383,9 +370,9 @@ bool LD2410Component::handle_ack_data_(uint8_t *buffer, int len) { | |||||||
|       this->light_function_ = LIGHT_FUNCTION_INT_TO_ENUM.at(buffer[10]); |       this->light_function_ = LIGHT_FUNCTION_INT_TO_ENUM.at(buffer[10]); | ||||||
|       this->light_threshold_ = buffer[11] * 1.0; |       this->light_threshold_ = buffer[11] * 1.0; | ||||||
|       this->out_pin_level_ = OUT_PIN_LEVEL_INT_TO_ENUM.at(buffer[12]); |       this->out_pin_level_ = OUT_PIN_LEVEL_INT_TO_ENUM.at(buffer[12]); | ||||||
|       ESP_LOGV(TAG, "Light function is: %s", const_cast<char *>(this->light_function_.c_str())); |       ESP_LOGV(TAG, "Light function: %s", const_cast<char *>(this->light_function_.c_str())); | ||||||
|       ESP_LOGV(TAG, "Light threshold is: %f", this->light_threshold_); |       ESP_LOGV(TAG, "Light threshold: %f", this->light_threshold_); | ||||||
|       ESP_LOGV(TAG, "Out pin level is: %s", const_cast<char *>(this->out_pin_level_.c_str())); |       ESP_LOGV(TAG, "Out pin level: %s", const_cast<char *>(this->out_pin_level_.c_str())); | ||||||
| #ifdef USE_SELECT | #ifdef USE_SELECT | ||||||
|       if (this->light_function_select_ != nullptr && this->light_function_select_->state != this->light_function_) { |       if (this->light_function_select_ != nullptr && this->light_function_select_->state != this->light_function_) { | ||||||
|         this->light_function_select_->publish_state(this->light_function_); |         this->light_function_select_->publish_state(this->light_function_); | ||||||
| @@ -406,11 +393,11 @@ bool LD2410Component::handle_ack_data_(uint8_t *buffer, int len) { | |||||||
|       if (len < 20) { |       if (len < 20) { | ||||||
|         return false; |         return false; | ||||||
|       } |       } | ||||||
|       this->mac_ = format_mac(buffer); |       this->mac_ = format_mac_address_pretty(&buffer[10]); | ||||||
|       ESP_LOGV(TAG, "MAC Address is: %s", const_cast<char *>(this->mac_.c_str())); |       ESP_LOGV(TAG, "MAC address: %s", this->mac_.c_str()); | ||||||
| #ifdef USE_TEXT_SENSOR | #ifdef USE_TEXT_SENSOR | ||||||
|       if (this->mac_text_sensor_ != nullptr) { |       if (this->mac_text_sensor_ != nullptr) { | ||||||
|         this->mac_text_sensor_->publish_state(this->mac_); |         this->mac_text_sensor_->publish_state(this->mac_ == NO_MAC ? UNKNOWN_MAC : this->mac_); | ||||||
|       } |       } | ||||||
| #endif | #endif | ||||||
| #ifdef USE_SWITCH | #ifdef USE_SWITCH | ||||||
| @@ -420,19 +407,19 @@ bool LD2410Component::handle_ack_data_(uint8_t *buffer, int len) { | |||||||
| #endif | #endif | ||||||
|       break; |       break; | ||||||
|     case lowbyte(CMD_GATE_SENS): |     case lowbyte(CMD_GATE_SENS): | ||||||
|       ESP_LOGV(TAG, "Handled sensitivity command"); |       ESP_LOGV(TAG, "Sensitivity"); | ||||||
|       break; |       break; | ||||||
|     case lowbyte(CMD_BLUETOOTH): |     case lowbyte(CMD_BLUETOOTH): | ||||||
|       ESP_LOGV(TAG, "Handled bluetooth command"); |       ESP_LOGV(TAG, "Bluetooth"); | ||||||
|       break; |       break; | ||||||
|     case lowbyte(CMD_SET_DISTANCE_RESOLUTION): |     case lowbyte(CMD_SET_DISTANCE_RESOLUTION): | ||||||
|       ESP_LOGV(TAG, "Handled set distance resolution command"); |       ESP_LOGV(TAG, "Set distance resolution"); | ||||||
|       break; |       break; | ||||||
|     case lowbyte(CMD_SET_LIGHT_CONTROL): |     case lowbyte(CMD_SET_LIGHT_CONTROL): | ||||||
|       ESP_LOGV(TAG, "Handled set light control command"); |       ESP_LOGV(TAG, "Set light control"); | ||||||
|       break; |       break; | ||||||
|     case lowbyte(CMD_BT_PASSWORD): |     case lowbyte(CMD_BT_PASSWORD): | ||||||
|       ESP_LOGV(TAG, "Handled set bluetooth password command"); |       ESP_LOGV(TAG, "Set bluetooth password"); | ||||||
|       break; |       break; | ||||||
|     case lowbyte(CMD_QUERY):  // Query parameters response |     case lowbyte(CMD_QUERY):  // Query parameters response | ||||||
|     { |     { | ||||||
| @@ -532,7 +519,7 @@ void LD2410Component::set_baud_rate(const std::string &state) { | |||||||
|  |  | ||||||
| void LD2410Component::set_bluetooth_password(const std::string &password) { | void LD2410Component::set_bluetooth_password(const std::string &password) { | ||||||
|   if (password.length() != 6) { |   if (password.length() != 6) { | ||||||
|     ESP_LOGE(TAG, "set_bluetooth_password(): invalid password length, must be exactly 6 chars '%s'", password.c_str()); |     ESP_LOGE(TAG, "Password must be exactly 6 chars"); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|   this->set_config_mode_(true); |   this->set_config_mode_(true); | ||||||
| @@ -544,7 +531,7 @@ void LD2410Component::set_bluetooth_password(const std::string &password) { | |||||||
|  |  | ||||||
| void LD2410Component::set_engineering_mode(bool enable) { | void LD2410Component::set_engineering_mode(bool enable) { | ||||||
|   this->set_config_mode_(true); |   this->set_config_mode_(true); | ||||||
|   last_engineering_mode_change_millis_ = millis(); |   last_engineering_mode_change_millis_ = App.get_loop_component_start_time(); | ||||||
|   uint8_t cmd = enable ? CMD_ENABLE_ENG : CMD_DISABLE_ENG; |   uint8_t cmd = enable ? CMD_ENABLE_ENG : CMD_DISABLE_ENG; | ||||||
|   this->send_command_(cmd, nullptr, 0); |   this->send_command_(cmd, nullptr, 0); | ||||||
|   this->set_config_mode_(false); |   this->set_config_mode_(false); | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| #include "ld2420.h" | #include "ld2420.h" | ||||||
|  | #include "esphome/core/application.h" | ||||||
| #include "esphome/core/helpers.h" | #include "esphome/core/helpers.h" | ||||||
|  |  | ||||||
| /* | /* | ||||||
| @@ -40,7 +41,7 @@ There are three documented parameters for modes: | |||||||
|   00 04 = Energy output mode |   00 04 = Energy output mode | ||||||
|     This mode outputs detailed signal energy values for each gate and the target distance. |     This mode outputs detailed signal energy values for each gate and the target distance. | ||||||
|     The data format consist of the following. |     The data format consist of the following. | ||||||
|     Header HH, Length LL, Persence PP, Distance DD, 16 Gate Energies EE, Footer FF |     Header HH, Length LL, Presence PP, Distance DD, 16 Gate Energies EE, Footer FF | ||||||
|     HH HH HH HH LL LL PP DD DD EE EE .. 16x   .. FF FF FF FF |     HH HH HH HH LL LL PP DD DD EE EE .. 16x   .. FF FF FF FF | ||||||
|     F4 F3 F2 F1 23 00 00 00 00 00 00 .. .. .. .. F8 F7 F6 F5 |     F4 F3 F2 F1 23 00 00 00 00 00 00 .. .. .. .. F8 F7 F6 F5 | ||||||
|   00 00 = debug output mode |   00 00 = debug output mode | ||||||
| @@ -67,10 +68,10 @@ float LD2420Component::get_setup_priority() const { return setup_priority::BUS; | |||||||
| void LD2420Component::dump_config() { | void LD2420Component::dump_config() { | ||||||
|   ESP_LOGCONFIG(TAG, |   ESP_LOGCONFIG(TAG, | ||||||
|                 "LD2420:\n" |                 "LD2420:\n" | ||||||
|                 "  Firmware Version : %7s\n" |                 "  Firmware version: %7s", | ||||||
|                 "LD2420 Number:", |  | ||||||
|                 this->ld2420_firmware_ver_); |                 this->ld2420_firmware_ver_); | ||||||
| #ifdef USE_NUMBER | #ifdef USE_NUMBER | ||||||
|  |   ESP_LOGCONFIG(TAG, "Number:"); | ||||||
|   LOG_NUMBER(TAG, "  Gate Timeout:", this->gate_timeout_number_); |   LOG_NUMBER(TAG, "  Gate Timeout:", this->gate_timeout_number_); | ||||||
|   LOG_NUMBER(TAG, "  Gate Max Distance:", this->max_gate_distance_number_); |   LOG_NUMBER(TAG, "  Gate Max Distance:", this->max_gate_distance_number_); | ||||||
|   LOG_NUMBER(TAG, "  Gate Min Distance:", this->min_gate_distance_number_); |   LOG_NUMBER(TAG, "  Gate Min Distance:", this->min_gate_distance_number_); | ||||||
| @@ -86,10 +87,10 @@ void LD2420Component::dump_config() { | |||||||
|   LOG_BUTTON(TAG, "  Factory Reset:", this->factory_reset_button_); |   LOG_BUTTON(TAG, "  Factory Reset:", this->factory_reset_button_); | ||||||
|   LOG_BUTTON(TAG, "  Restart Module:", this->restart_module_button_); |   LOG_BUTTON(TAG, "  Restart Module:", this->restart_module_button_); | ||||||
| #endif | #endif | ||||||
|   ESP_LOGCONFIG(TAG, "LD2420 Select:"); |   ESP_LOGCONFIG(TAG, "Select:"); | ||||||
|   LOG_SELECT(TAG, "  Operating Mode", this->operating_selector_); |   LOG_SELECT(TAG, "  Operating Mode", this->operating_selector_); | ||||||
|   if (this->get_firmware_int_(ld2420_firmware_ver_) < CALIBRATE_VERSION_MIN) { |   if (LD2420Component::get_firmware_int(this->ld2420_firmware_ver_) < CALIBRATE_VERSION_MIN) { | ||||||
|     ESP_LOGW(TAG, "LD2420 Firmware Version %s and older are only supported in Simple Mode", ld2420_firmware_ver_); |     ESP_LOGW(TAG, "Firmware version %s and older supports Simple Mode only", this->ld2420_firmware_ver_); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -102,7 +103,7 @@ uint8_t LD2420Component::calc_checksum(void *data, size_t size) { | |||||||
|   return checksum; |   return checksum; | ||||||
| } | } | ||||||
|  |  | ||||||
| int LD2420Component::get_firmware_int_(const char *version_string) { | int LD2420Component::get_firmware_int(const char *version_string) { | ||||||
|   std::string version_str = version_string; |   std::string version_str = version_string; | ||||||
|   if (version_str[0] == 'v') { |   if (version_str[0] == 'v') { | ||||||
|     version_str = version_str.substr(1); |     version_str = version_str.substr(1); | ||||||
| @@ -115,7 +116,7 @@ int LD2420Component::get_firmware_int_(const char *version_string) { | |||||||
| void LD2420Component::setup() { | void LD2420Component::setup() { | ||||||
|   ESP_LOGCONFIG(TAG, "Running setup"); |   ESP_LOGCONFIG(TAG, "Running setup"); | ||||||
|   if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) { |   if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) { | ||||||
|     ESP_LOGE(TAG, "LD2420 module has failed to respond, check baud rate and serial connections."); |     ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); | ||||||
|     this->mark_failed(); |     this->mark_failed(); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| @@ -127,7 +128,7 @@ void LD2420Component::setup() { | |||||||
|   const char *pfw = this->ld2420_firmware_ver_; |   const char *pfw = this->ld2420_firmware_ver_; | ||||||
|   std::string fw_str(pfw); |   std::string fw_str(pfw); | ||||||
|  |  | ||||||
|   for (auto &listener : listeners_) { |   for (auto &listener : this->listeners_) { | ||||||
|     listener->on_fw_version(fw_str); |     listener->on_fw_version(fw_str); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -137,11 +138,11 @@ void LD2420Component::setup() { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   memcpy(&this->new_config, &this->current_config, sizeof(this->current_config)); |   memcpy(&this->new_config, &this->current_config, sizeof(this->current_config)); | ||||||
|   if (get_firmware_int_(ld2420_firmware_ver_) < CALIBRATE_VERSION_MIN) { |   if (LD2420Component::get_firmware_int(this->ld2420_firmware_ver_) < CALIBRATE_VERSION_MIN) { | ||||||
|     this->set_operating_mode(OP_SIMPLE_MODE_STRING); |     this->set_operating_mode(OP_SIMPLE_MODE_STRING); | ||||||
|     this->operating_selector_->publish_state(OP_SIMPLE_MODE_STRING); |     this->operating_selector_->publish_state(OP_SIMPLE_MODE_STRING); | ||||||
|     this->set_mode_(CMD_SYSTEM_MODE_SIMPLE); |     this->set_mode_(CMD_SYSTEM_MODE_SIMPLE); | ||||||
|     ESP_LOGW(TAG, "LD2420 Frimware Version %s and older are only supported in Simple Mode", ld2420_firmware_ver_); |     ESP_LOGW(TAG, "Firmware version %s and older supports Simple Mode only", this->ld2420_firmware_ver_); | ||||||
|   } else { |   } else { | ||||||
|     this->set_mode_(CMD_SYSTEM_MODE_ENERGY); |     this->set_mode_(CMD_SYSTEM_MODE_ENERGY); | ||||||
|     this->operating_selector_->publish_state(OP_NORMAL_MODE_STRING); |     this->operating_selector_->publish_state(OP_NORMAL_MODE_STRING); | ||||||
| @@ -151,18 +152,17 @@ void LD2420Component::setup() { | |||||||
| #endif | #endif | ||||||
|   this->set_system_mode(this->system_mode_); |   this->set_system_mode(this->system_mode_); | ||||||
|   this->set_config_mode(false); |   this->set_config_mode(false); | ||||||
|   ESP_LOGCONFIG(TAG, "LD2420 setup complete."); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void LD2420Component::apply_config_action() { | void LD2420Component::apply_config_action() { | ||||||
|   const uint8_t checksum = calc_checksum(&this->new_config, sizeof(this->new_config)); |   const uint8_t checksum = calc_checksum(&this->new_config, sizeof(this->new_config)); | ||||||
|   if (checksum == calc_checksum(&this->current_config, sizeof(this->current_config))) { |   if (checksum == calc_checksum(&this->current_config, sizeof(this->current_config))) { | ||||||
|     ESP_LOGCONFIG(TAG, "No configuration change detected"); |     ESP_LOGD(TAG, "No configuration change detected"); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|   ESP_LOGCONFIG(TAG, "Reconfiguring LD2420"); |   ESP_LOGD(TAG, "Reconfiguring"); | ||||||
|   if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) { |   if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) { | ||||||
|     ESP_LOGE(TAG, "LD2420 module has failed to respond, check baud rate and serial connections."); |     ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); | ||||||
|     this->mark_failed(); |     this->mark_failed(); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| @@ -178,13 +178,12 @@ void LD2420Component::apply_config_action() { | |||||||
|   this->set_system_mode(this->system_mode_); |   this->set_system_mode(this->system_mode_); | ||||||
|   this->set_config_mode(false);  // Disable config mode to save new values in LD2420 nvm |   this->set_config_mode(false);  // Disable config mode to save new values in LD2420 nvm | ||||||
|   this->set_operating_mode(OP_NORMAL_MODE_STRING); |   this->set_operating_mode(OP_NORMAL_MODE_STRING); | ||||||
|   ESP_LOGCONFIG(TAG, "LD2420 reconfig complete."); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void LD2420Component::factory_reset_action() { | void LD2420Component::factory_reset_action() { | ||||||
|   ESP_LOGCONFIG(TAG, "Setting factory defaults"); |   ESP_LOGD(TAG, "Setting factory defaults"); | ||||||
|   if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) { |   if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) { | ||||||
|     ESP_LOGE(TAG, "LD2420 module has failed to respond, check baud rate and serial connections."); |     ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); | ||||||
|     this->mark_failed(); |     this->mark_failed(); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| @@ -207,18 +206,16 @@ void LD2420Component::factory_reset_action() { | |||||||
|   this->init_gate_config_numbers(); |   this->init_gate_config_numbers(); | ||||||
|   this->refresh_gate_config_numbers(); |   this->refresh_gate_config_numbers(); | ||||||
| #endif | #endif | ||||||
|   ESP_LOGCONFIG(TAG, "LD2420 factory reset complete."); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void LD2420Component::restart_module_action() { | void LD2420Component::restart_module_action() { | ||||||
|   ESP_LOGCONFIG(TAG, "Restarting LD2420 module"); |   ESP_LOGD(TAG, "Restarting"); | ||||||
|   this->send_module_restart(); |   this->send_module_restart(); | ||||||
|   this->set_timeout(250, [this]() { |   this->set_timeout(250, [this]() { | ||||||
|     this->set_config_mode(true); |     this->set_config_mode(true); | ||||||
|     this->set_system_mode(system_mode_); |     this->set_system_mode(this->system_mode_); | ||||||
|     this->set_config_mode(false); |     this->set_config_mode(false); | ||||||
|   }); |   }); | ||||||
|   ESP_LOGCONFIG(TAG, "LD2420 Restarted."); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void LD2420Component::revert_config_action() { | void LD2420Component::revert_config_action() { | ||||||
| @@ -226,18 +223,18 @@ void LD2420Component::revert_config_action() { | |||||||
| #ifdef USE_NUMBER | #ifdef USE_NUMBER | ||||||
|   this->init_gate_config_numbers(); |   this->init_gate_config_numbers(); | ||||||
| #endif | #endif | ||||||
|   ESP_LOGCONFIG(TAG, "Reverted config number edits."); |   ESP_LOGD(TAG, "Reverted config number edits"); | ||||||
| } | } | ||||||
|  |  | ||||||
| void LD2420Component::loop() { | void LD2420Component::loop() { | ||||||
|   // If there is a active send command do not process it here, the send command call will handle it. |   // If there is a active send command do not process it here, the send command call will handle it. | ||||||
|   if (!get_cmd_active_()) { |   if (!this->get_cmd_active_()) { | ||||||
|     if (!available()) |     if (!this->available()) | ||||||
|       return; |       return; | ||||||
|     static uint8_t buffer[2048]; |     static uint8_t buffer[2048]; | ||||||
|     static uint8_t rx_data; |     static uint8_t rx_data; | ||||||
|     while (available()) { |     while (this->available()) { | ||||||
|       rx_data = read(); |       rx_data = this->read(); | ||||||
|       this->readline_(rx_data, buffer, sizeof(buffer)); |       this->readline_(rx_data, buffer, sizeof(buffer)); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @@ -292,7 +289,7 @@ void LD2420Component::report_gate_data() { | |||||||
|  |  | ||||||
| void LD2420Component::set_operating_mode(const std::string &state) { | void LD2420Component::set_operating_mode(const std::string &state) { | ||||||
|   // If unsupported firmware ignore mode select |   // If unsupported firmware ignore mode select | ||||||
|   if (get_firmware_int_(ld2420_firmware_ver_) >= CALIBRATE_VERSION_MIN) { |   if (LD2420Component::get_firmware_int(ld2420_firmware_ver_) >= CALIBRATE_VERSION_MIN) { | ||||||
|     this->current_operating_mode = OP_MODE_TO_UINT.at(state); |     this->current_operating_mode = OP_MODE_TO_UINT.at(state); | ||||||
|     // Entering Auto Calibrate we need to clear the privoiuos data collection |     // Entering Auto Calibrate we need to clear the privoiuos data collection | ||||||
|     this->operating_selector_->publish_state(state); |     this->operating_selector_->publish_state(state); | ||||||
| @@ -365,13 +362,13 @@ void LD2420Component::handle_energy_mode_(uint8_t *buffer, int len) { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Resonable refresh rate for home assistant database size health |   // Resonable refresh rate for home assistant database size health | ||||||
|   const int32_t current_millis = millis(); |   const int32_t current_millis = App.get_loop_component_start_time(); | ||||||
|   if (current_millis - this->last_periodic_millis < REFRESH_RATE_MS) |   if (current_millis - this->last_periodic_millis < REFRESH_RATE_MS) | ||||||
|     return; |     return; | ||||||
|   this->last_periodic_millis = current_millis; |   this->last_periodic_millis = current_millis; | ||||||
|   for (auto &listener : this->listeners_) { |   for (auto &listener : this->listeners_) { | ||||||
|     listener->on_distance(get_distance_()); |     listener->on_distance(this->get_distance_()); | ||||||
|     listener->on_presence(get_presence_()); |     listener->on_presence(this->get_presence_()); | ||||||
|     listener->on_energy(this->gate_energy_, sizeof(this->gate_energy_) / sizeof(this->gate_energy_[0])); |     listener->on_energy(this->gate_energy_, sizeof(this->gate_energy_) / sizeof(this->gate_energy_[0])); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -392,9 +389,9 @@ void LD2420Component::handle_simple_mode_(const uint8_t *inbuf, int len) { | |||||||
|   char outbuf[bufsize]{0}; |   char outbuf[bufsize]{0}; | ||||||
|   while (true) { |   while (true) { | ||||||
|     if (inbuf[pos - 2] == 'O' && inbuf[pos - 1] == 'F' && inbuf[pos] == 'F') { |     if (inbuf[pos - 2] == 'O' && inbuf[pos - 1] == 'F' && inbuf[pos] == 'F') { | ||||||
|       set_presence_(false); |       this->set_presence_(false); | ||||||
|     } else if (inbuf[pos - 1] == 'O' && inbuf[pos] == 'N') { |     } else if (inbuf[pos - 1] == 'O' && inbuf[pos] == 'N') { | ||||||
|       set_presence_(true); |       this->set_presence_(true); | ||||||
|     } |     } | ||||||
|     if (inbuf[pos] >= '0' && inbuf[pos] <= '9') { |     if (inbuf[pos] >= '0' && inbuf[pos] <= '9') { | ||||||
|       if (index < bufsize - 1) { |       if (index < bufsize - 1) { | ||||||
| @@ -411,18 +408,18 @@ void LD2420Component::handle_simple_mode_(const uint8_t *inbuf, int len) { | |||||||
|   } |   } | ||||||
|   outbuf[index] = '\0'; |   outbuf[index] = '\0'; | ||||||
|   if (index > 1) |   if (index > 1) | ||||||
|     set_distance_(strtol(outbuf, &endptr, 10)); |     this->set_distance_(strtol(outbuf, &endptr, 10)); | ||||||
|  |  | ||||||
|   if (get_mode_() == CMD_SYSTEM_MODE_SIMPLE) { |   if (this->get_mode_() == CMD_SYSTEM_MODE_SIMPLE) { | ||||||
|     // Resonable refresh rate for home assistant database size health |     // Resonable refresh rate for home assistant database size health | ||||||
|     const int32_t current_millis = millis(); |     const int32_t current_millis = App.get_loop_component_start_time(); | ||||||
|     if (current_millis - this->last_normal_periodic_millis < REFRESH_RATE_MS) |     if (current_millis - this->last_normal_periodic_millis < REFRESH_RATE_MS) | ||||||
|       return; |       return; | ||||||
|     this->last_normal_periodic_millis = current_millis; |     this->last_normal_periodic_millis = current_millis; | ||||||
|     for (auto &listener : this->listeners_) |     for (auto &listener : this->listeners_) | ||||||
|       listener->on_distance(get_distance_()); |       listener->on_distance(this->get_distance_()); | ||||||
|     for (auto &listener : this->listeners_) |     for (auto &listener : this->listeners_) | ||||||
|       listener->on_presence(get_presence_()); |       listener->on_presence(this->get_presence_()); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -433,10 +430,10 @@ void LD2420Component::handle_ack_data_(uint8_t *buffer, int len) { | |||||||
|   uint8_t data_element = 0; |   uint8_t data_element = 0; | ||||||
|   uint16_t data_pos = 0; |   uint16_t data_pos = 0; | ||||||
|   if (this->cmd_reply_.length > CMD_MAX_BYTES) { |   if (this->cmd_reply_.length > CMD_MAX_BYTES) { | ||||||
|     ESP_LOGW(TAG, "LD2420 reply - received command reply frame is corrupt, length exceeds %d bytes.", CMD_MAX_BYTES); |     ESP_LOGW(TAG, "Reply frame too long"); | ||||||
|     return; |     return; | ||||||
|   } else if (this->cmd_reply_.length < 2) { |   } else if (this->cmd_reply_.length < 2) { | ||||||
|     ESP_LOGW(TAG, "LD2420 reply - received command frame is corrupt, length is less than 2 bytes."); |     ESP_LOGW(TAG, "Command frame too short"); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|   memcpy(&this->cmd_reply_.error, &buffer[CMD_ERROR_WORD], sizeof(this->cmd_reply_.error)); |   memcpy(&this->cmd_reply_.error, &buffer[CMD_ERROR_WORD], sizeof(this->cmd_reply_.error)); | ||||||
| @@ -447,13 +444,13 @@ void LD2420Component::handle_ack_data_(uint8_t *buffer, int len) { | |||||||
|   this->cmd_reply_.ack = true; |   this->cmd_reply_.ack = true; | ||||||
|   switch ((uint16_t) this->cmd_reply_.command) { |   switch ((uint16_t) this->cmd_reply_.command) { | ||||||
|     case (CMD_ENABLE_CONF): |     case (CMD_ENABLE_CONF): | ||||||
|       ESP_LOGD(TAG, "LD2420 reply - set config enable: CMD = %2X %s", CMD_ENABLE_CONF, result); |       ESP_LOGV(TAG, "Set config enable: CMD = %2X %s", CMD_ENABLE_CONF, result); | ||||||
|       break; |       break; | ||||||
|     case (CMD_DISABLE_CONF): |     case (CMD_DISABLE_CONF): | ||||||
|       ESP_LOGD(TAG, "LD2420 reply - set config disable: CMD = %2X %s", CMD_DISABLE_CONF, result); |       ESP_LOGV(TAG, "Set config disable: CMD = %2X %s", CMD_DISABLE_CONF, result); | ||||||
|       break; |       break; | ||||||
|     case (CMD_READ_REGISTER): |     case (CMD_READ_REGISTER): | ||||||
|       ESP_LOGD(TAG, "LD2420 reply - read register: CMD = %2X %s", CMD_READ_REGISTER, result); |       ESP_LOGV(TAG, "Read register: CMD = %2X %s", CMD_READ_REGISTER, result); | ||||||
|       // TODO Read/Write register is not implemented yet, this will get flushed out to a proper header file |       // TODO Read/Write register is not implemented yet, this will get flushed out to a proper header file | ||||||
|       data_pos = 0x0A; |       data_pos = 0x0A; | ||||||
|       for (uint16_t index = 0; index < (CMD_REG_DATA_REPLY_SIZE *  // NOLINT |       for (uint16_t index = 0; index < (CMD_REG_DATA_REPLY_SIZE *  // NOLINT | ||||||
| @@ -465,13 +462,13 @@ void LD2420Component::handle_ack_data_(uint8_t *buffer, int len) { | |||||||
|       } |       } | ||||||
|       break; |       break; | ||||||
|     case (CMD_WRITE_REGISTER): |     case (CMD_WRITE_REGISTER): | ||||||
|       ESP_LOGD(TAG, "LD2420 reply - write register: CMD = %2X %s", CMD_WRITE_REGISTER, result); |       ESP_LOGV(TAG, "Write register: CMD = %2X %s", CMD_WRITE_REGISTER, result); | ||||||
|       break; |       break; | ||||||
|     case (CMD_WRITE_ABD_PARAM): |     case (CMD_WRITE_ABD_PARAM): | ||||||
|       ESP_LOGD(TAG, "LD2420 reply - write gate parameter(s): %2X %s", CMD_WRITE_ABD_PARAM, result); |       ESP_LOGV(TAG, "Write gate parameter(s): %2X %s", CMD_WRITE_ABD_PARAM, result); | ||||||
|       break; |       break; | ||||||
|     case (CMD_READ_ABD_PARAM): |     case (CMD_READ_ABD_PARAM): | ||||||
|       ESP_LOGD(TAG, "LD2420 reply - read gate parameter(s): %2X %s", CMD_READ_ABD_PARAM, result); |       ESP_LOGV(TAG, "Read gate parameter(s): %2X %s", CMD_READ_ABD_PARAM, result); | ||||||
|       data_pos = CMD_ABD_DATA_REPLY_START; |       data_pos = CMD_ABD_DATA_REPLY_START; | ||||||
|       for (uint16_t index = 0; index < (CMD_ABD_DATA_REPLY_SIZE *  // NOLINT |       for (uint16_t index = 0; index < (CMD_ABD_DATA_REPLY_SIZE *  // NOLINT | ||||||
|                                         ((buffer[CMD_FRAME_DATA_LENGTH] - 4) / CMD_ABD_DATA_REPLY_SIZE)); |                                         ((buffer[CMD_FRAME_DATA_LENGTH] - 4) / CMD_ABD_DATA_REPLY_SIZE)); | ||||||
| @@ -483,11 +480,11 @@ void LD2420Component::handle_ack_data_(uint8_t *buffer, int len) { | |||||||
|       } |       } | ||||||
|       break; |       break; | ||||||
|     case (CMD_WRITE_SYS_PARAM): |     case (CMD_WRITE_SYS_PARAM): | ||||||
|       ESP_LOGD(TAG, "LD2420 reply - set system parameter(s): %2X %s", CMD_WRITE_SYS_PARAM, result); |       ESP_LOGV(TAG, "Set system parameter(s): %2X %s", CMD_WRITE_SYS_PARAM, result); | ||||||
|       break; |       break; | ||||||
|     case (CMD_READ_VERSION): |     case (CMD_READ_VERSION): | ||||||
|       memcpy(this->ld2420_firmware_ver_, &buffer[12], buffer[10]); |       memcpy(this->ld2420_firmware_ver_, &buffer[12], buffer[10]); | ||||||
|       ESP_LOGD(TAG, "LD2420 reply - module firmware version: %7s %s", this->ld2420_firmware_ver_, result); |       ESP_LOGV(TAG, "Firmware version: %7s %s", this->ld2420_firmware_ver_, result); | ||||||
|       break; |       break; | ||||||
|     default: |     default: | ||||||
|       break; |       break; | ||||||
| @@ -533,7 +530,7 @@ int LD2420Component::send_cmd_from_array(CmdFrameT frame) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     while (!this->cmd_reply_.ack) { |     while (!this->cmd_reply_.ack) { | ||||||
|       while (available()) { |       while (this->available()) { | ||||||
|         this->readline_(read(), ack_buffer, sizeof(ack_buffer)); |         this->readline_(read(), ack_buffer, sizeof(ack_buffer)); | ||||||
|       } |       } | ||||||
|       delay_microseconds_safe(1450); |       delay_microseconds_safe(1450); | ||||||
| @@ -548,7 +545,7 @@ int LD2420Component::send_cmd_from_array(CmdFrameT frame) { | |||||||
|     if (this->cmd_reply_.ack) |     if (this->cmd_reply_.ack) | ||||||
|       retry = 0; |       retry = 0; | ||||||
|     if (this->cmd_reply_.error > 0) |     if (this->cmd_reply_.error > 0) | ||||||
|       handle_cmd_error(error); |       this->handle_cmd_error(error); | ||||||
|   } |   } | ||||||
|   return error; |   return error; | ||||||
| } | } | ||||||
| @@ -563,7 +560,7 @@ uint8_t LD2420Component::set_config_mode(bool enable) { | |||||||
|     cmd_frame.data_length += sizeof(CMD_PROTOCOL_VER); |     cmd_frame.data_length += sizeof(CMD_PROTOCOL_VER); | ||||||
|   } |   } | ||||||
|   cmd_frame.footer = CMD_FRAME_FOOTER; |   cmd_frame.footer = CMD_FRAME_FOOTER; | ||||||
|   ESP_LOGD(TAG, "Sending set config %s command: %2X", enable ? "enable" : "disable", cmd_frame.command); |   ESP_LOGV(TAG, "Sending set config %s command: %2X", enable ? "enable" : "disable", cmd_frame.command); | ||||||
|   return this->send_cmd_from_array(cmd_frame); |   return this->send_cmd_from_array(cmd_frame); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -576,7 +573,7 @@ void LD2420Component::ld2420_restart() { | |||||||
|   cmd_frame.header = CMD_FRAME_HEADER; |   cmd_frame.header = CMD_FRAME_HEADER; | ||||||
|   cmd_frame.command = CMD_RESTART; |   cmd_frame.command = CMD_RESTART; | ||||||
|   cmd_frame.footer = CMD_FRAME_FOOTER; |   cmd_frame.footer = CMD_FRAME_FOOTER; | ||||||
|   ESP_LOGD(TAG, "Sending restart command: %2X", cmd_frame.command); |   ESP_LOGV(TAG, "Sending restart command: %2X", cmd_frame.command); | ||||||
|   this->send_cmd_from_array(cmd_frame); |   this->send_cmd_from_array(cmd_frame); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -588,7 +585,7 @@ void LD2420Component::get_reg_value_(uint16_t reg) { | |||||||
|   cmd_frame.data[1] = reg; |   cmd_frame.data[1] = reg; | ||||||
|   cmd_frame.data_length += 2; |   cmd_frame.data_length += 2; | ||||||
|   cmd_frame.footer = CMD_FRAME_FOOTER; |   cmd_frame.footer = CMD_FRAME_FOOTER; | ||||||
|   ESP_LOGD(TAG, "Sending read register %4X command: %2X", reg, cmd_frame.command); |   ESP_LOGV(TAG, "Sending read register %4X command: %2X", reg, cmd_frame.command); | ||||||
|   this->send_cmd_from_array(cmd_frame); |   this->send_cmd_from_array(cmd_frame); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -602,11 +599,11 @@ void LD2420Component::set_reg_value(uint16_t reg, uint16_t value) { | |||||||
|   memcpy(&cmd_frame.data[cmd_frame.data_length], &value, sizeof(CMD_REG_DATA_REPLY_SIZE)); |   memcpy(&cmd_frame.data[cmd_frame.data_length], &value, sizeof(CMD_REG_DATA_REPLY_SIZE)); | ||||||
|   cmd_frame.data_length += 2; |   cmd_frame.data_length += 2; | ||||||
|   cmd_frame.footer = CMD_FRAME_FOOTER; |   cmd_frame.footer = CMD_FRAME_FOOTER; | ||||||
|   ESP_LOGD(TAG, "Sending write register %4X command: %2X data = %4X", reg, cmd_frame.command, value); |   ESP_LOGV(TAG, "Sending write register %4X command: %2X data = %4X", reg, cmd_frame.command, value); | ||||||
|   this->send_cmd_from_array(cmd_frame); |   this->send_cmd_from_array(cmd_frame); | ||||||
| } | } | ||||||
|  |  | ||||||
| void LD2420Component::handle_cmd_error(uint8_t error) { ESP_LOGI(TAG, "Command failed: %s", ERR_MESSAGE[error]); } | void LD2420Component::handle_cmd_error(uint8_t error) { ESP_LOGE(TAG, "Command failed: %s", ERR_MESSAGE[error]); } | ||||||
|  |  | ||||||
| int LD2420Component::get_gate_threshold_(uint8_t gate) { | int LD2420Component::get_gate_threshold_(uint8_t gate) { | ||||||
|   uint8_t error; |   uint8_t error; | ||||||
| @@ -619,7 +616,7 @@ int LD2420Component::get_gate_threshold_(uint8_t gate) { | |||||||
|   memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_GATE_STILL_THRESH[gate], sizeof(CMD_GATE_STILL_THRESH[gate])); |   memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_GATE_STILL_THRESH[gate], sizeof(CMD_GATE_STILL_THRESH[gate])); | ||||||
|   cmd_frame.data_length += 2; |   cmd_frame.data_length += 2; | ||||||
|   cmd_frame.footer = CMD_FRAME_FOOTER; |   cmd_frame.footer = CMD_FRAME_FOOTER; | ||||||
|   ESP_LOGD(TAG, "Sending read gate %d high/low theshold command: %2X", gate, cmd_frame.command); |   ESP_LOGV(TAG, "Sending read gate %d high/low threshold command: %2X", gate, cmd_frame.command); | ||||||
|   error = this->send_cmd_from_array(cmd_frame); |   error = this->send_cmd_from_array(cmd_frame); | ||||||
|   if (error == 0) { |   if (error == 0) { | ||||||
|     this->current_config.move_thresh[gate] = cmd_reply_.data[0]; |     this->current_config.move_thresh[gate] = cmd_reply_.data[0]; | ||||||
| @@ -644,7 +641,7 @@ int LD2420Component::get_min_max_distances_timeout_() { | |||||||
|          sizeof(CMD_TIMEOUT_REG));  // Register: global delay time |          sizeof(CMD_TIMEOUT_REG));  // Register: global delay time | ||||||
|   cmd_frame.data_length += sizeof(CMD_TIMEOUT_REG); |   cmd_frame.data_length += sizeof(CMD_TIMEOUT_REG); | ||||||
|   cmd_frame.footer = CMD_FRAME_FOOTER; |   cmd_frame.footer = CMD_FRAME_FOOTER; | ||||||
|   ESP_LOGD(TAG, "Sending read gate min max and timeout command: %2X", cmd_frame.command); |   ESP_LOGV(TAG, "Sending read gate min max and timeout command: %2X", cmd_frame.command); | ||||||
|   error = this->send_cmd_from_array(cmd_frame); |   error = this->send_cmd_from_array(cmd_frame); | ||||||
|   if (error == 0) { |   if (error == 0) { | ||||||
|     this->current_config.min_gate = (uint16_t) cmd_reply_.data[0]; |     this->current_config.min_gate = (uint16_t) cmd_reply_.data[0]; | ||||||
| @@ -667,9 +664,9 @@ void LD2420Component::set_system_mode(uint16_t mode) { | |||||||
|   memcpy(&cmd_frame.data[cmd_frame.data_length], &unknown_parm, sizeof(unknown_parm)); |   memcpy(&cmd_frame.data[cmd_frame.data_length], &unknown_parm, sizeof(unknown_parm)); | ||||||
|   cmd_frame.data_length += sizeof(unknown_parm); |   cmd_frame.data_length += sizeof(unknown_parm); | ||||||
|   cmd_frame.footer = CMD_FRAME_FOOTER; |   cmd_frame.footer = CMD_FRAME_FOOTER; | ||||||
|   ESP_LOGD(TAG, "Sending write system mode command: %2X", cmd_frame.command); |   ESP_LOGV(TAG, "Sending write system mode command: %2X", cmd_frame.command); | ||||||
|   if (this->send_cmd_from_array(cmd_frame) == 0) |   if (this->send_cmd_from_array(cmd_frame) == 0) | ||||||
|     set_mode_(mode); |     this->set_mode_(mode); | ||||||
| } | } | ||||||
|  |  | ||||||
| void LD2420Component::get_firmware_version_() { | void LD2420Component::get_firmware_version_() { | ||||||
| @@ -679,7 +676,7 @@ void LD2420Component::get_firmware_version_() { | |||||||
|   cmd_frame.command = CMD_READ_VERSION; |   cmd_frame.command = CMD_READ_VERSION; | ||||||
|   cmd_frame.footer = CMD_FRAME_FOOTER; |   cmd_frame.footer = CMD_FRAME_FOOTER; | ||||||
|  |  | ||||||
|   ESP_LOGD(TAG, "Sending read firmware version command: %2X", cmd_frame.command); |   ESP_LOGV(TAG, "Sending read firmware version command: %2X", cmd_frame.command); | ||||||
|   this->send_cmd_from_array(cmd_frame); |   this->send_cmd_from_array(cmd_frame); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -712,7 +709,7 @@ void LD2420Component::set_min_max_distances_timeout(uint32_t max_gate_distance, | |||||||
|   cmd_frame.data_length += sizeof(timeout); |   cmd_frame.data_length += sizeof(timeout); | ||||||
|   cmd_frame.footer = CMD_FRAME_FOOTER; |   cmd_frame.footer = CMD_FRAME_FOOTER; | ||||||
|  |  | ||||||
|   ESP_LOGD(TAG, "Sending write gate min max and timeout command: %2X", cmd_frame.command); |   ESP_LOGV(TAG, "Sending write gate min max and timeout command: %2X", cmd_frame.command); | ||||||
|   this->send_cmd_from_array(cmd_frame); |   this->send_cmd_from_array(cmd_frame); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -738,7 +735,7 @@ void LD2420Component::set_gate_threshold(uint8_t gate) { | |||||||
|          sizeof(this->new_config.still_thresh[gate])); |          sizeof(this->new_config.still_thresh[gate])); | ||||||
|   cmd_frame.data_length += sizeof(this->new_config.still_thresh[gate]); |   cmd_frame.data_length += sizeof(this->new_config.still_thresh[gate]); | ||||||
|   cmd_frame.footer = CMD_FRAME_FOOTER; |   cmd_frame.footer = CMD_FRAME_FOOTER; | ||||||
|   ESP_LOGD(TAG, "Sending set gate %4X sensitivity command: %2X", gate, cmd_frame.command); |   ESP_LOGV(TAG, "Sending set gate %4X sensitivity command: %2X", gate, cmd_frame.command); | ||||||
|   this->send_cmd_from_array(cmd_frame); |   this->send_cmd_from_array(cmd_frame); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -179,7 +179,7 @@ class LD2420Component : public Component, public uart::UARTDevice { | |||||||
|   void set_operating_mode(const std::string &state); |   void set_operating_mode(const std::string &state); | ||||||
|   void auto_calibrate_sensitivity(); |   void auto_calibrate_sensitivity(); | ||||||
|   void update_radar_data(uint16_t const *gate_energy, uint8_t sample_number); |   void update_radar_data(uint16_t const *gate_energy, uint8_t sample_number); | ||||||
|   uint8_t calc_checksum(void *data, size_t size); |   static uint8_t calc_checksum(void *data, size_t size); | ||||||
|  |  | ||||||
|   RegConfigT current_config; |   RegConfigT current_config; | ||||||
|   RegConfigT new_config; |   RegConfigT new_config; | ||||||
| @@ -222,7 +222,7 @@ class LD2420Component : public Component, public uart::UARTDevice { | |||||||
|     volatile bool ack; |     volatile bool ack; | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   int get_firmware_int_(const char *version_string); |   static int get_firmware_int(const char *version_string); | ||||||
|   void get_firmware_version_(); |   void get_firmware_version_(); | ||||||
|   int get_gate_threshold_(uint8_t gate); |   int get_gate_threshold_(uint8_t gate); | ||||||
|   void get_reg_value_(uint16_t reg); |   void get_reg_value_(uint16_t reg); | ||||||
|   | |||||||
| @@ -6,7 +6,9 @@ | |||||||
| #ifdef USE_SENSOR | #ifdef USE_SENSOR | ||||||
| #include "esphome/components/sensor/sensor.h" | #include "esphome/components/sensor/sensor.h" | ||||||
| #endif | #endif | ||||||
|  | #include "esphome/core/application.h" | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  |  | ||||||
| #define highbyte(val) (uint8_t)((val) >> 8) | #define highbyte(val) (uint8_t)((val) >> 8) | ||||||
| #define lowbyte(val) (uint8_t)((val) &0xff) | #define lowbyte(val) (uint8_t)((val) &0xff) | ||||||
| @@ -96,11 +98,6 @@ static inline std::string get_direction(int16_t speed) { | |||||||
|   return STATIONARY; |   return STATIONARY; | ||||||
| } | } | ||||||
|  |  | ||||||
| static inline std::string format_mac(uint8_t *buffer) { |  | ||||||
|   return str_snprintf("%02X:%02X:%02X:%02X:%02X:%02X", 17, buffer[10], buffer[11], buffer[12], buffer[13], buffer[14], |  | ||||||
|                       buffer[15]); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline std::string format_version(uint8_t *buffer) { | static inline std::string format_version(uint8_t *buffer) { | ||||||
|   return str_sprintf("%u.%02X.%02X%02X%02X%02X", buffer[13], buffer[12], buffer[17], buffer[16], buffer[15], |   return str_sprintf("%u.%02X.%02X%02X%02X%02X", buffer[13], buffer[12], buffer[17], buffer[16], buffer[15], | ||||||
|                      buffer[14]); |                      buffer[14]); | ||||||
| @@ -120,7 +117,7 @@ void LD2450Component::setup() { | |||||||
| } | } | ||||||
|  |  | ||||||
| void LD2450Component::dump_config() { | void LD2450Component::dump_config() { | ||||||
|   ESP_LOGCONFIG(TAG, "HLK-LD2450 Human motion tracking radar module:"); |   ESP_LOGCONFIG(TAG, "LD2450:"); | ||||||
| #ifdef USE_BINARY_SENSOR | #ifdef USE_BINARY_SENSOR | ||||||
|   LOG_BINARY_SENSOR("  ", "TargetBinarySensor", this->target_binary_sensor_); |   LOG_BINARY_SENSOR("  ", "TargetBinarySensor", this->target_binary_sensor_); | ||||||
|   LOG_BINARY_SENSOR("  ", "MovingTargetBinarySensor", this->moving_target_binary_sensor_); |   LOG_BINARY_SENSOR("  ", "MovingTargetBinarySensor", this->moving_target_binary_sensor_); | ||||||
| @@ -189,9 +186,9 @@ void LD2450Component::dump_config() { | |||||||
|   LOG_NUMBER("  ", "PresenceTimeoutNumber", this->presence_timeout_number_); |   LOG_NUMBER("  ", "PresenceTimeoutNumber", this->presence_timeout_number_); | ||||||
| #endif | #endif | ||||||
|   ESP_LOGCONFIG(TAG, |   ESP_LOGCONFIG(TAG, | ||||||
|                 "  Throttle : %ums\n" |                 "  Throttle: %ums\n" | ||||||
|                 "  MAC Address : %s\n" |                 "  MAC Address: %s\n" | ||||||
|                 "  Firmware version : %s", |                 "  Firmware version: %s", | ||||||
|                 this->throttle_, const_cast<char *>(this->mac_.c_str()), const_cast<char *>(this->version_.c_str())); |                 this->throttle_, const_cast<char *>(this->mac_.c_str()), const_cast<char *>(this->version_.c_str())); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -266,8 +263,7 @@ bool LD2450Component::get_timeout_status_(uint32_t check_millis) { | |||||||
|   if (this->timeout_ == 0) { |   if (this->timeout_ == 0) { | ||||||
|     this->timeout_ = ld2450::convert_seconds_to_ms(DEFAULT_PRESENCE_TIMEOUT); |     this->timeout_ = ld2450::convert_seconds_to_ms(DEFAULT_PRESENCE_TIMEOUT); | ||||||
|   } |   } | ||||||
|   auto current_millis = millis(); |   return App.get_loop_component_start_time() - check_millis >= this->timeout_; | ||||||
|   return current_millis - check_millis >= this->timeout_; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // Extract, store and publish zone details LD2450 buffer | // Extract, store and publish zone details LD2450 buffer | ||||||
| @@ -354,25 +350,24 @@ void LD2450Component::send_command_(uint8_t command, const uint8_t *command_valu | |||||||
| //   Header       Target 1                  Target 2                  Target 3                  End | //   Header       Target 1                  Target 2                  Target 3                  End | ||||||
| void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) { | void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) { | ||||||
|   if (len < 29) {  // header (4 bytes) + 8 x 3 target data + footer (2 bytes) |   if (len < 29) {  // header (4 bytes) + 8 x 3 target data + footer (2 bytes) | ||||||
|     ESP_LOGE(TAG, "Periodic data: invalid message length"); |     ESP_LOGE(TAG, "Invalid message length"); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|   if (buffer[0] != 0xAA || buffer[1] != 0xFF || buffer[2] != 0x03 || buffer[3] != 0x00) {  // header |   if (buffer[0] != 0xAA || buffer[1] != 0xFF || buffer[2] != 0x03 || buffer[3] != 0x00) {  // header | ||||||
|     ESP_LOGE(TAG, "Periodic data: invalid message header"); |     ESP_LOGE(TAG, "Invalid message header"); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|   if (buffer[len - 2] != 0x55 || buffer[len - 1] != 0xCC) {  // footer |   if (buffer[len - 2] != 0x55 || buffer[len - 1] != 0xCC) {  // footer | ||||||
|     ESP_LOGE(TAG, "Periodic data: invalid message footer"); |     ESP_LOGE(TAG, "Invalid message footer"); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   auto current_millis = millis(); |   if (App.get_loop_component_start_time() - this->last_periodic_millis_ < this->throttle_) { | ||||||
|   if (current_millis - this->last_periodic_millis_ < this->throttle_) { |  | ||||||
|     ESP_LOGV(TAG, "Throttling: %d", this->throttle_); |     ESP_LOGV(TAG, "Throttling: %d", this->throttle_); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   this->last_periodic_millis_ = current_millis; |   this->last_periodic_millis_ = App.get_loop_component_start_time(); | ||||||
|  |  | ||||||
|   int16_t target_count = 0; |   int16_t target_count = 0; | ||||||
|   int16_t still_target_count = 0; |   int16_t still_target_count = 0; | ||||||
| @@ -555,13 +550,13 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) { | |||||||
| #ifdef USE_SENSOR | #ifdef USE_SENSOR | ||||||
|   // For presence timeout check |   // For presence timeout check | ||||||
|   if (target_count > 0) { |   if (target_count > 0) { | ||||||
|     this->presence_millis_ = millis(); |     this->presence_millis_ = App.get_loop_component_start_time(); | ||||||
|   } |   } | ||||||
|   if (moving_target_count > 0) { |   if (moving_target_count > 0) { | ||||||
|     this->moving_presence_millis_ = millis(); |     this->moving_presence_millis_ = App.get_loop_component_start_time(); | ||||||
|   } |   } | ||||||
|   if (still_target_count > 0) { |   if (still_target_count > 0) { | ||||||
|     this->still_presence_millis_ = millis(); |     this->still_presence_millis_ = App.get_loop_component_start_time(); | ||||||
|   } |   } | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
| @@ -569,31 +564,31 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) { | |||||||
| bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) { | bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) { | ||||||
|   ESP_LOGV(TAG, "Handling ack data for command %02X", buffer[COMMAND]); |   ESP_LOGV(TAG, "Handling ack data for command %02X", buffer[COMMAND]); | ||||||
|   if (len < 10) { |   if (len < 10) { | ||||||
|     ESP_LOGE(TAG, "Ack data: invalid length"); |     ESP_LOGE(TAG, "Invalid ack length"); | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|   if (buffer[0] != 0xFD || buffer[1] != 0xFC || buffer[2] != 0xFB || buffer[3] != 0xFA) {  // frame header |   if (buffer[0] != 0xFD || buffer[1] != 0xFC || buffer[2] != 0xFB || buffer[3] != 0xFA) {  // frame header | ||||||
|     ESP_LOGE(TAG, "Ack data: invalid header (command %02X)", buffer[COMMAND]); |     ESP_LOGE(TAG, "Invalid ack header (command %02X)", buffer[COMMAND]); | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|   if (buffer[COMMAND_STATUS] != 0x01) { |   if (buffer[COMMAND_STATUS] != 0x01) { | ||||||
|     ESP_LOGE(TAG, "Ack data: invalid status"); |     ESP_LOGE(TAG, "Invalid ack status"); | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|   if (buffer[8] || buffer[9]) { |   if (buffer[8] || buffer[9]) { | ||||||
|     ESP_LOGE(TAG, "Ack data: last buffer was %u, %u", buffer[8], buffer[9]); |     ESP_LOGE(TAG, "Last buffer was %u, %u", buffer[8], buffer[9]); | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   switch (buffer[COMMAND]) { |   switch (buffer[COMMAND]) { | ||||||
|     case lowbyte(CMD_ENABLE_CONF): |     case lowbyte(CMD_ENABLE_CONF): | ||||||
|       ESP_LOGV(TAG, "Got enable conf command"); |       ESP_LOGV(TAG, "Enable conf command"); | ||||||
|       break; |       break; | ||||||
|     case lowbyte(CMD_DISABLE_CONF): |     case lowbyte(CMD_DISABLE_CONF): | ||||||
|       ESP_LOGV(TAG, "Got disable conf command"); |       ESP_LOGV(TAG, "Disable conf command"); | ||||||
|       break; |       break; | ||||||
|     case lowbyte(CMD_SET_BAUD_RATE): |     case lowbyte(CMD_SET_BAUD_RATE): | ||||||
|       ESP_LOGV(TAG, "Got baud rate change command"); |       ESP_LOGV(TAG, "Baud rate change command"); | ||||||
| #ifdef USE_SELECT | #ifdef USE_SELECT | ||||||
|       if (this->baud_rate_select_ != nullptr) { |       if (this->baud_rate_select_ != nullptr) { | ||||||
|         ESP_LOGV(TAG, "Change baud rate to %s", this->baud_rate_select_->state.c_str()); |         ESP_LOGV(TAG, "Change baud rate to %s", this->baud_rate_select_->state.c_str()); | ||||||
| @@ -613,7 +608,7 @@ bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) { | |||||||
|       if (len < 20) { |       if (len < 20) { | ||||||
|         return false; |         return false; | ||||||
|       } |       } | ||||||
|       this->mac_ = ld2450::format_mac(buffer); |       this->mac_ = format_mac_address_pretty(&buffer[10]); | ||||||
|       ESP_LOGV(TAG, "MAC address: %s", this->mac_.c_str()); |       ESP_LOGV(TAG, "MAC address: %s", this->mac_.c_str()); | ||||||
| #ifdef USE_TEXT_SENSOR | #ifdef USE_TEXT_SENSOR | ||||||
|       if (this->mac_text_sensor_ != nullptr) { |       if (this->mac_text_sensor_ != nullptr) { | ||||||
| @@ -622,15 +617,15 @@ bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) { | |||||||
| #endif | #endif | ||||||
| #ifdef USE_SWITCH | #ifdef USE_SWITCH | ||||||
|       if (this->bluetooth_switch_ != nullptr) { |       if (this->bluetooth_switch_ != nullptr) { | ||||||
|         this->bluetooth_switch_->publish_state(this->mac_ != NO_MAC); |         this->bluetooth_switch_->publish_state(this->mac_ != UNKNOWN_MAC); | ||||||
|       } |       } | ||||||
| #endif | #endif | ||||||
|       break; |       break; | ||||||
|     case lowbyte(CMD_BLUETOOTH): |     case lowbyte(CMD_BLUETOOTH): | ||||||
|       ESP_LOGV(TAG, "Got Bluetooth command"); |       ESP_LOGV(TAG, "Bluetooth command"); | ||||||
|       break; |       break; | ||||||
|     case lowbyte(CMD_SINGLE_TARGET_MODE): |     case lowbyte(CMD_SINGLE_TARGET_MODE): | ||||||
|       ESP_LOGV(TAG, "Got single target conf command"); |       ESP_LOGV(TAG, "Single target conf command"); | ||||||
| #ifdef USE_SWITCH | #ifdef USE_SWITCH | ||||||
|       if (this->multi_target_switch_ != nullptr) { |       if (this->multi_target_switch_ != nullptr) { | ||||||
|         this->multi_target_switch_->publish_state(false); |         this->multi_target_switch_->publish_state(false); | ||||||
| @@ -638,7 +633,7 @@ bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) { | |||||||
| #endif | #endif | ||||||
|       break; |       break; | ||||||
|     case lowbyte(CMD_MULTI_TARGET_MODE): |     case lowbyte(CMD_MULTI_TARGET_MODE): | ||||||
|       ESP_LOGV(TAG, "Got multi target conf command"); |       ESP_LOGV(TAG, "Multi target conf command"); | ||||||
| #ifdef USE_SWITCH | #ifdef USE_SWITCH | ||||||
|       if (this->multi_target_switch_ != nullptr) { |       if (this->multi_target_switch_ != nullptr) { | ||||||
|         this->multi_target_switch_->publish_state(true); |         this->multi_target_switch_->publish_state(true); | ||||||
| @@ -646,7 +641,7 @@ bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) { | |||||||
| #endif | #endif | ||||||
|       break; |       break; | ||||||
|     case lowbyte(CMD_QUERY_TARGET_MODE): |     case lowbyte(CMD_QUERY_TARGET_MODE): | ||||||
|       ESP_LOGV(TAG, "Got query target tracking mode command"); |       ESP_LOGV(TAG, "Query target tracking mode command"); | ||||||
| #ifdef USE_SWITCH | #ifdef USE_SWITCH | ||||||
|       if (this->multi_target_switch_ != nullptr) { |       if (this->multi_target_switch_ != nullptr) { | ||||||
|         this->multi_target_switch_->publish_state(buffer[10] == 0x02); |         this->multi_target_switch_->publish_state(buffer[10] == 0x02); | ||||||
| @@ -654,7 +649,7 @@ bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) { | |||||||
| #endif | #endif | ||||||
|       break; |       break; | ||||||
|     case lowbyte(CMD_QUERY_ZONE): |     case lowbyte(CMD_QUERY_ZONE): | ||||||
|       ESP_LOGV(TAG, "Got query zone conf command"); |       ESP_LOGV(TAG, "Query zone conf command"); | ||||||
|       this->zone_type_ = std::stoi(std::to_string(buffer[10]), nullptr, 16); |       this->zone_type_ = std::stoi(std::to_string(buffer[10]), nullptr, 16); | ||||||
|       this->publish_zone_type(); |       this->publish_zone_type(); | ||||||
| #ifdef USE_SELECT | #ifdef USE_SELECT | ||||||
| @@ -674,7 +669,7 @@ bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) { | |||||||
|       this->process_zone_(buffer); |       this->process_zone_(buffer); | ||||||
|       break; |       break; | ||||||
|     case lowbyte(CMD_SET_ZONE): |     case lowbyte(CMD_SET_ZONE): | ||||||
|       ESP_LOGV(TAG, "Got set zone conf command"); |       ESP_LOGV(TAG, "Set zone conf command"); | ||||||
|       this->query_zone_info(); |       this->query_zone_info(); | ||||||
|       break; |       break; | ||||||
|     default: |     default: | ||||||
|   | |||||||
| @@ -64,6 +64,14 @@ class ModbusDevice { | |||||||
|     this->parent_->send(this->address_, function, start_address, number_of_entities, payload_len, payload); |     this->parent_->send(this->address_, function, start_address, number_of_entities, payload_len, payload); | ||||||
|   } |   } | ||||||
|   void send_raw(const std::vector<uint8_t> &payload) { this->parent_->send_raw(payload); } |   void send_raw(const std::vector<uint8_t> &payload) { this->parent_->send_raw(payload); } | ||||||
|  |   void send_error(uint8_t function_code, uint8_t exception_code) { | ||||||
|  |     std::vector<uint8_t> error_response; | ||||||
|  |     error_response.reserve(3); | ||||||
|  |     error_response.push_back(this->address_); | ||||||
|  |     error_response.push_back(function_code | 0x80); | ||||||
|  |     error_response.push_back(exception_code); | ||||||
|  |     this->send_raw(error_response); | ||||||
|  |   } | ||||||
|   // If more than one device is connected block sending a new command before a response is received |   // If more than one device is connected block sending a new command before a response is received | ||||||
|   bool waiting_for_response() { return parent_->waiting_for_response != 0; } |   bool waiting_for_response() { return parent_->waiting_for_response != 0; } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -112,6 +112,22 @@ TYPE_REGISTER_MAP = { | |||||||
|     "FP32_R": 2, |     "FP32_R": 2, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | CPP_TYPE_REGISTER_MAP = { | ||||||
|  |     "RAW": cg.uint16, | ||||||
|  |     "U_WORD": cg.uint16, | ||||||
|  |     "S_WORD": cg.int16, | ||||||
|  |     "U_DWORD": cg.uint32, | ||||||
|  |     "U_DWORD_R": cg.uint32, | ||||||
|  |     "S_DWORD": cg.int32, | ||||||
|  |     "S_DWORD_R": cg.int32, | ||||||
|  |     "U_QWORD": cg.uint64, | ||||||
|  |     "U_QWORD_R": cg.uint64, | ||||||
|  |     "S_QWORD": cg.int64, | ||||||
|  |     "S_QWORD_R": cg.int64, | ||||||
|  |     "FP32": cg.float_, | ||||||
|  |     "FP32_R": cg.float_, | ||||||
|  | } | ||||||
|  |  | ||||||
| ModbusCommandSentTrigger = modbus_controller_ns.class_( | ModbusCommandSentTrigger = modbus_controller_ns.class_( | ||||||
|     "ModbusCommandSentTrigger", automation.Trigger.template(cg.int_, cg.int_) |     "ModbusCommandSentTrigger", automation.Trigger.template(cg.int_, cg.int_) | ||||||
| ) | ) | ||||||
| @@ -285,21 +301,24 @@ async def to_code(config): | |||||||
|     cg.add(var.set_offline_skip_updates(config[CONF_OFFLINE_SKIP_UPDATES])) |     cg.add(var.set_offline_skip_updates(config[CONF_OFFLINE_SKIP_UPDATES])) | ||||||
|     if CONF_SERVER_REGISTERS in config: |     if CONF_SERVER_REGISTERS in config: | ||||||
|         for server_register in config[CONF_SERVER_REGISTERS]: |         for server_register in config[CONF_SERVER_REGISTERS]: | ||||||
|  |             server_register_var = cg.new_Pvariable( | ||||||
|  |                 server_register[CONF_ID], | ||||||
|  |                 server_register[CONF_ADDRESS], | ||||||
|  |                 server_register[CONF_VALUE_TYPE], | ||||||
|  |                 TYPE_REGISTER_MAP[server_register[CONF_VALUE_TYPE]], | ||||||
|  |             ) | ||||||
|  |             cpp_type = CPP_TYPE_REGISTER_MAP[server_register[CONF_VALUE_TYPE]] | ||||||
|             cg.add( |             cg.add( | ||||||
|                 var.add_server_register( |                 server_register_var.set_read_lambda( | ||||||
|                     cg.new_Pvariable( |                     cg.TemplateArguments(cpp_type), | ||||||
|                         server_register[CONF_ID], |                     await cg.process_lambda( | ||||||
|                         server_register[CONF_ADDRESS], |                         server_register[CONF_READ_LAMBDA], | ||||||
|                         server_register[CONF_VALUE_TYPE], |                         [(cg.uint16, "address")], | ||||||
|                         TYPE_REGISTER_MAP[server_register[CONF_VALUE_TYPE]], |                         return_type=cpp_type, | ||||||
|                         await cg.process_lambda( |                     ), | ||||||
|                             server_register[CONF_READ_LAMBDA], |  | ||||||
|                             [], |  | ||||||
|                             return_type=cg.float_, |  | ||||||
|                         ), |  | ||||||
|                     ) |  | ||||||
|                 ) |                 ) | ||||||
|             ) |             ) | ||||||
|  |             cg.add(var.add_server_register(server_register_var)) | ||||||
|     await register_modbus_device(var, config) |     await register_modbus_device(var, config) | ||||||
|     for conf in config.get(CONF_ON_COMMAND_SENT, []): |     for conf in config.get(CONF_ON_COMMAND_SENT, []): | ||||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) |         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||||
|   | |||||||
| @@ -117,12 +117,17 @@ void ModbusController::on_modbus_read_registers(uint8_t function_code, uint16_t | |||||||
|     bool found = false; |     bool found = false; | ||||||
|     for (auto *server_register : this->server_registers_) { |     for (auto *server_register : this->server_registers_) { | ||||||
|       if (server_register->address == current_address) { |       if (server_register->address == current_address) { | ||||||
|         float value = server_register->read_lambda(); |         if (!server_register->read_lambda) { | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |         int64_t value = server_register->read_lambda(); | ||||||
|  |         ESP_LOGD(TAG, "Matched register. Address: 0x%02X. Value type: %zu. Register count: %u. Value: %s.", | ||||||
|  |                  server_register->address, static_cast<size_t>(server_register->value_type), | ||||||
|  |                  server_register->register_count, server_register->format_value(value).c_str()); | ||||||
|  |  | ||||||
|         ESP_LOGD(TAG, "Matched register. Address: 0x%02X. Value type: %zu. Register count: %u. Value: %0.1f.", |         std::vector<uint16_t> payload; | ||||||
|                  server_register->address, static_cast<uint8_t>(server_register->value_type), |         payload.reserve(server_register->register_count * 2); | ||||||
|                  server_register->register_count, value); |         number_to_payload(payload, value, server_register->value_type); | ||||||
|         std::vector<uint16_t> payload = float_to_payload(value, server_register->value_type); |  | ||||||
|         sixteen_bit_response.insert(sixteen_bit_response.end(), payload.cbegin(), payload.cend()); |         sixteen_bit_response.insert(sixteen_bit_response.end(), payload.cbegin(), payload.cend()); | ||||||
|         current_address += server_register->register_count; |         current_address += server_register->register_count; | ||||||
|         found = true; |         found = true; | ||||||
| @@ -132,11 +137,7 @@ void ModbusController::on_modbus_read_registers(uint8_t function_code, uint16_t | |||||||
|  |  | ||||||
|     if (!found) { |     if (!found) { | ||||||
|       ESP_LOGW(TAG, "Could not match any register to address %02X. Sending exception response.", current_address); |       ESP_LOGW(TAG, "Could not match any register to address %02X. Sending exception response.", current_address); | ||||||
|       std::vector<uint8_t> error_response; |       send_error(function_code, 0x02); | ||||||
|       error_response.push_back(this->address_); |  | ||||||
|       error_response.push_back(0x81); |  | ||||||
|       error_response.push_back(0x02); |  | ||||||
|       this->send_raw(error_response); |  | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -63,6 +63,10 @@ enum class SensorValueType : uint8_t { | |||||||
|   FP32_R = 0xD |   FP32_R = 0xD | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | inline bool value_type_is_float(SensorValueType v) { | ||||||
|  |   return v == SensorValueType::FP32 || v == SensorValueType::FP32_R; | ||||||
|  | } | ||||||
|  |  | ||||||
| inline ModbusFunctionCode modbus_register_read_function(ModbusRegisterType reg_type) { | inline ModbusFunctionCode modbus_register_read_function(ModbusRegisterType reg_type) { | ||||||
|   switch (reg_type) { |   switch (reg_type) { | ||||||
|     case ModbusRegisterType::COIL: |     case ModbusRegisterType::COIL: | ||||||
| @@ -253,18 +257,53 @@ class SensorItem { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| class ServerRegister { | class ServerRegister { | ||||||
|  |   using ReadLambda = std::function<int64_t()>; | ||||||
|  |  | ||||||
|  public: |  public: | ||||||
|   ServerRegister(uint16_t address, SensorValueType value_type, uint8_t register_count, |   ServerRegister(uint16_t address, SensorValueType value_type, uint8_t register_count) { | ||||||
|                  std::function<float()> read_lambda) { |  | ||||||
|     this->address = address; |     this->address = address; | ||||||
|     this->value_type = value_type; |     this->value_type = value_type; | ||||||
|     this->register_count = register_count; |     this->register_count = register_count; | ||||||
|     this->read_lambda = std::move(read_lambda); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   template<typename T> void set_read_lambda(const std::function<T(uint16_t address)> &&user_read_lambda) { | ||||||
|  |     this->read_lambda = [this, user_read_lambda]() -> int64_t { | ||||||
|  |       T user_value = user_read_lambda(this->address); | ||||||
|  |       if constexpr (std::is_same_v<T, float>) { | ||||||
|  |         return bit_cast<uint32_t>(user_value); | ||||||
|  |       } else { | ||||||
|  |         return static_cast<int64_t>(user_value); | ||||||
|  |       } | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Formats a raw value into a string representation based on the value type for debugging | ||||||
|  |   std::string format_value(int64_t value) const { | ||||||
|  |     switch (this->value_type) { | ||||||
|  |       case SensorValueType::U_WORD: | ||||||
|  |       case SensorValueType::U_DWORD: | ||||||
|  |       case SensorValueType::U_DWORD_R: | ||||||
|  |       case SensorValueType::U_QWORD: | ||||||
|  |       case SensorValueType::U_QWORD_R: | ||||||
|  |         return std::to_string(static_cast<uint64_t>(value)); | ||||||
|  |       case SensorValueType::S_WORD: | ||||||
|  |       case SensorValueType::S_DWORD: | ||||||
|  |       case SensorValueType::S_DWORD_R: | ||||||
|  |       case SensorValueType::S_QWORD: | ||||||
|  |       case SensorValueType::S_QWORD_R: | ||||||
|  |         return std::to_string(value); | ||||||
|  |       case SensorValueType::FP32_R: | ||||||
|  |       case SensorValueType::FP32: | ||||||
|  |         return str_sprintf("%.1f", bit_cast<float>(static_cast<uint32_t>(value))); | ||||||
|  |       default: | ||||||
|  |         return std::to_string(value); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|   uint16_t address{0}; |   uint16_t address{0}; | ||||||
|   SensorValueType value_type{SensorValueType::RAW}; |   SensorValueType value_type{SensorValueType::RAW}; | ||||||
|   uint8_t register_count{0}; |   uint8_t register_count{0}; | ||||||
|   std::function<float()> read_lambda; |   ReadLambda read_lambda; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // ModbusController::create_register_ranges_ tries to optimize register range | // ModbusController::create_register_ranges_ tries to optimize register range | ||||||
| @@ -444,7 +483,7 @@ class ModbusController : public PollingComponent, public modbus::ModbusDevice { | |||||||
|   void on_modbus_data(const std::vector<uint8_t> &data) override; |   void on_modbus_data(const std::vector<uint8_t> &data) override; | ||||||
|   /// called when a modbus error response was received |   /// called when a modbus error response was received | ||||||
|   void on_modbus_error(uint8_t function_code, uint8_t exception_code) override; |   void on_modbus_error(uint8_t function_code, uint8_t exception_code) override; | ||||||
|   /// called when a modbus request (function code 3 or 4) was parsed without errors |   /// called when a modbus request (function code 0x03 or 0x04) was parsed without errors | ||||||
|   void on_modbus_read_registers(uint8_t function_code, uint16_t start_address, uint16_t number_of_registers) final; |   void on_modbus_read_registers(uint8_t function_code, uint16_t start_address, uint16_t number_of_registers) final; | ||||||
|   /// default delegate called by process_modbus_data when a response has retrieved from the incoming queue |   /// default delegate called by process_modbus_data when a response has retrieved from the incoming queue | ||||||
|   void on_register_data(ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data); |   void on_register_data(ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data); | ||||||
| @@ -529,7 +568,7 @@ inline float payload_to_float(const std::vector<uint8_t> &data, const SensorItem | |||||||
|   int64_t number = payload_to_number(data, item.sensor_value_type, item.offset, item.bitmask); |   int64_t number = payload_to_number(data, item.sensor_value_type, item.offset, item.bitmask); | ||||||
|  |  | ||||||
|   float float_value; |   float float_value; | ||||||
|   if (item.sensor_value_type == SensorValueType::FP32 || item.sensor_value_type == SensorValueType::FP32_R) { |   if (value_type_is_float(item.sensor_value_type)) { | ||||||
|     float_value = bit_cast<float>(static_cast<uint32_t>(number)); |     float_value = bit_cast<float>(static_cast<uint32_t>(number)); | ||||||
|   } else { |   } else { | ||||||
|     float_value = static_cast<float>(number); |     float_value = static_cast<float>(number); | ||||||
| @@ -541,7 +580,7 @@ inline float payload_to_float(const std::vector<uint8_t> &data, const SensorItem | |||||||
| inline std::vector<uint16_t> float_to_payload(float value, SensorValueType value_type) { | inline std::vector<uint16_t> float_to_payload(float value, SensorValueType value_type) { | ||||||
|   int64_t val; |   int64_t val; | ||||||
|  |  | ||||||
|   if (value_type == SensorValueType::FP32 || value_type == SensorValueType::FP32_R) { |   if (value_type_is_float(value_type)) { | ||||||
|     val = bit_cast<uint32_t>(value); |     val = bit_cast<uint32_t>(value); | ||||||
|   } else { |   } else { | ||||||
|     val = llroundf(value); |     val = llroundf(value); | ||||||
|   | |||||||
| @@ -17,7 +17,8 @@ enum class MQTTClientDisconnectReason : int8_t { | |||||||
|   MQTT_MALFORMED_CREDENTIALS = 4, |   MQTT_MALFORMED_CREDENTIALS = 4, | ||||||
|   MQTT_NOT_AUTHORIZED = 5, |   MQTT_NOT_AUTHORIZED = 5, | ||||||
|   ESP8266_NOT_ENOUGH_SPACE = 6, |   ESP8266_NOT_ENOUGH_SPACE = 6, | ||||||
|   TLS_BAD_FINGERPRINT = 7 |   TLS_BAD_FINGERPRINT = 7, | ||||||
|  |   DNS_RESOLVE_ERROR = 8 | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /// internal struct for MQTT messages. | /// internal struct for MQTT messages. | ||||||
|   | |||||||
| @@ -229,6 +229,8 @@ void MQTTClientComponent::check_dnslookup_() { | |||||||
|   if (this->dns_resolve_error_) { |   if (this->dns_resolve_error_) { | ||||||
|     ESP_LOGW(TAG, "Couldn't resolve IP address for '%s'", this->credentials_.address.c_str()); |     ESP_LOGW(TAG, "Couldn't resolve IP address for '%s'", this->credentials_.address.c_str()); | ||||||
|     this->state_ = MQTT_CLIENT_DISCONNECTED; |     this->state_ = MQTT_CLIENT_DISCONNECTED; | ||||||
|  |     this->disconnect_reason_ = MQTTClientDisconnectReason::DNS_RESOLVE_ERROR; | ||||||
|  |     this->on_disconnect_.call(MQTTClientDisconnectReason::DNS_RESOLVE_ERROR); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -698,7 +700,9 @@ void MQTTClientComponent::set_on_connect(mqtt_on_connect_callback_t &&callback) | |||||||
| } | } | ||||||
|  |  | ||||||
| void MQTTClientComponent::set_on_disconnect(mqtt_on_disconnect_callback_t &&callback) { | void MQTTClientComponent::set_on_disconnect(mqtt_on_disconnect_callback_t &&callback) { | ||||||
|  |   auto callback_copy = callback; | ||||||
|   this->mqtt_backend_.set_on_disconnect(std::forward<mqtt_on_disconnect_callback_t>(callback)); |   this->mqtt_backend_.set_on_disconnect(std::forward<mqtt_on_disconnect_callback_t>(callback)); | ||||||
|  |   this->on_disconnect_.add(std::move(callback_copy)); | ||||||
| } | } | ||||||
|  |  | ||||||
| #if ASYNC_TCP_SSL_ENABLED | #if ASYNC_TCP_SSL_ENABLED | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ | |||||||
| #include "esphome/components/network/ip_address.h" | #include "esphome/components/network/ip_address.h" | ||||||
| #include "esphome/core/automation.h" | #include "esphome/core/automation.h" | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
| #include "esphome/core/log.h" | #include "esphome/core/log.h" | ||||||
| #if defined(USE_ESP32) | #if defined(USE_ESP32) | ||||||
| #include "mqtt_backend_esp32.h" | #include "mqtt_backend_esp32.h" | ||||||
| @@ -334,6 +335,7 @@ class MQTTClientComponent : public Component { | |||||||
|   uint32_t connect_begin_; |   uint32_t connect_begin_; | ||||||
|   uint32_t last_connected_{0}; |   uint32_t last_connected_{0}; | ||||||
|   optional<MQTTClientDisconnectReason> disconnect_reason_{}; |   optional<MQTTClientDisconnectReason> disconnect_reason_{}; | ||||||
|  |   CallbackManager<MQTTBackend::on_disconnect_callback_t> on_disconnect_; | ||||||
|  |  | ||||||
|   bool publish_nan_as_none_{false}; |   bool publish_nan_as_none_{false}; | ||||||
|   bool wait_for_connection_{false}; |   bool wait_for_connection_{false}; | ||||||
|   | |||||||
| @@ -23,16 +23,22 @@ std::string state_class_to_string(StateClass state_class) { | |||||||
| Sensor::Sensor() : state(NAN), raw_state(NAN) {} | Sensor::Sensor() : state(NAN), raw_state(NAN) {} | ||||||
|  |  | ||||||
| int8_t Sensor::get_accuracy_decimals() { | int8_t Sensor::get_accuracy_decimals() { | ||||||
|   if (this->accuracy_decimals_.has_value()) |   if (this->sensor_flags_.has_accuracy_override) | ||||||
|     return *this->accuracy_decimals_; |     return this->accuracy_decimals_; | ||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
| void Sensor::set_accuracy_decimals(int8_t accuracy_decimals) { this->accuracy_decimals_ = accuracy_decimals; } | void Sensor::set_accuracy_decimals(int8_t accuracy_decimals) { | ||||||
|  |   this->accuracy_decimals_ = accuracy_decimals; | ||||||
|  |   this->sensor_flags_.has_accuracy_override = true; | ||||||
|  | } | ||||||
|  |  | ||||||
| void Sensor::set_state_class(StateClass state_class) { this->state_class_ = state_class; } | void Sensor::set_state_class(StateClass state_class) { | ||||||
|  |   this->state_class_ = state_class; | ||||||
|  |   this->sensor_flags_.has_state_class_override = true; | ||||||
|  | } | ||||||
| StateClass Sensor::get_state_class() { | StateClass Sensor::get_state_class() { | ||||||
|   if (this->state_class_.has_value()) |   if (this->sensor_flags_.has_state_class_override) | ||||||
|     return *this->state_class_; |     return this->state_class_; | ||||||
|   return StateClass::STATE_CLASS_NONE; |   return StateClass::STATE_CLASS_NONE; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -80,9 +80,9 @@ class Sensor : public EntityBase, public EntityBase_DeviceClass, public EntityBa | |||||||
|    * state changes to the database when they are published, even if the state is the |    * state changes to the database when they are published, even if the state is the | ||||||
|    * same as before. |    * same as before. | ||||||
|    */ |    */ | ||||||
|   bool get_force_update() const { return force_update_; } |   bool get_force_update() const { return sensor_flags_.force_update; } | ||||||
|   /// Set force update mode. |   /// Set force update mode. | ||||||
|   void set_force_update(bool force_update) { force_update_ = force_update; } |   void set_force_update(bool force_update) { sensor_flags_.force_update = force_update; } | ||||||
|  |  | ||||||
|   /// Add a filter to the filter chain. Will be appended to the back. |   /// Add a filter to the filter chain. Will be appended to the back. | ||||||
|   void add_filter(Filter *filter); |   void add_filter(Filter *filter); | ||||||
| @@ -155,9 +155,17 @@ class Sensor : public EntityBase, public EntityBase_DeviceClass, public EntityBa | |||||||
|  |  | ||||||
|   Filter *filter_list_{nullptr};  ///< Store all active filters. |   Filter *filter_list_{nullptr};  ///< Store all active filters. | ||||||
|  |  | ||||||
|   optional<int8_t> accuracy_decimals_;                  ///< Accuracy in decimals override |   // Group small members together to avoid padding | ||||||
|   optional<StateClass> state_class_{STATE_CLASS_NONE};  ///< State class override |   int8_t accuracy_decimals_{-1};              ///< Accuracy in decimals (-1 = not set) | ||||||
|   bool force_update_{false};                            ///< Force update mode |   StateClass state_class_{STATE_CLASS_NONE};  ///< State class (STATE_CLASS_NONE = not set) | ||||||
|  |  | ||||||
|  |   // Bit-packed flags for sensor-specific settings | ||||||
|  |   struct SensorFlags { | ||||||
|  |     uint8_t has_accuracy_override : 1; | ||||||
|  |     uint8_t has_state_class_override : 1; | ||||||
|  |     uint8_t force_update : 1; | ||||||
|  |     uint8_t reserved : 5;  // Reserved for future use | ||||||
|  |   } sensor_flags_{}; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace sensor | }  // namespace sensor | ||||||
|   | |||||||
| @@ -741,11 +741,6 @@ void WiFiComponent::set_power_save_mode(WiFiPowerSaveMode power_save) { this->po | |||||||
|  |  | ||||||
| void WiFiComponent::set_passive_scan(bool passive) { this->passive_scan_ = passive; } | void WiFiComponent::set_passive_scan(bool passive) { this->passive_scan_ = passive; } | ||||||
|  |  | ||||||
| std::string WiFiComponent::format_mac_addr(const uint8_t *mac) { |  | ||||||
|   char buf[20]; |  | ||||||
|   sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); |  | ||||||
|   return buf; |  | ||||||
| } |  | ||||||
| bool WiFiComponent::is_captive_portal_active_() { | bool WiFiComponent::is_captive_portal_active_() { | ||||||
| #ifdef USE_CAPTIVE_PORTAL | #ifdef USE_CAPTIVE_PORTAL | ||||||
|   return captive_portal::global_captive_portal != nullptr && captive_portal::global_captive_portal->is_active(); |   return captive_portal::global_captive_portal != nullptr && captive_portal::global_captive_portal->is_active(); | ||||||
|   | |||||||
| @@ -321,8 +321,6 @@ class WiFiComponent : public Component { | |||||||
|   int32_t get_wifi_channel(); |   int32_t get_wifi_channel(); | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   static std::string format_mac_addr(const uint8_t mac[6]); |  | ||||||
|  |  | ||||||
| #ifdef USE_WIFI_AP | #ifdef USE_WIFI_AP | ||||||
|   void setup_ap_config_(); |   void setup_ap_config_(); | ||||||
| #endif  // USE_WIFI_AP | #endif  // USE_WIFI_AP | ||||||
|   | |||||||
| @@ -550,7 +550,7 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ | |||||||
|       memcpy(buf, it.ssid, it.ssid_len); |       memcpy(buf, it.ssid, it.ssid_len); | ||||||
|       buf[it.ssid_len] = '\0'; |       buf[it.ssid_len] = '\0'; | ||||||
|       ESP_LOGV(TAG, "Connected ssid='%s' bssid=" LOG_SECRET("%s") " channel=%u, authmode=%s", buf, |       ESP_LOGV(TAG, "Connected ssid='%s' bssid=" LOG_SECRET("%s") " channel=%u, authmode=%s", buf, | ||||||
|                format_mac_addr(it.bssid).c_str(), it.channel, get_auth_mode_str(it.authmode)); |                format_mac_address_pretty(it.bssid).c_str(), it.channel, get_auth_mode_str(it.authmode)); | ||||||
| #if USE_NETWORK_IPV6 | #if USE_NETWORK_IPV6 | ||||||
|       this->set_timeout(100, [] { WiFi.enableIPv6(); }); |       this->set_timeout(100, [] { WiFi.enableIPv6(); }); | ||||||
| #endif /* USE_NETWORK_IPV6 */ | #endif /* USE_NETWORK_IPV6 */ | ||||||
| @@ -566,7 +566,7 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ | |||||||
|         ESP_LOGW(TAG, "Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf); |         ESP_LOGW(TAG, "Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf); | ||||||
|       } else { |       } else { | ||||||
|         ESP_LOGW(TAG, "Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf, |         ESP_LOGW(TAG, "Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf, | ||||||
|                  format_mac_addr(it.bssid).c_str(), get_disconnect_reason_str(it.reason)); |                  format_mac_address_pretty(it.bssid).c_str(), get_disconnect_reason_str(it.reason)); | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       uint8_t reason = it.reason; |       uint8_t reason = it.reason; | ||||||
| @@ -636,13 +636,13 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ | |||||||
|     case ESPHOME_EVENT_ID_WIFI_AP_STACONNECTED: { |     case ESPHOME_EVENT_ID_WIFI_AP_STACONNECTED: { | ||||||
|       auto it = info.wifi_sta_connected; |       auto it = info.wifi_sta_connected; | ||||||
|       auto &mac = it.bssid; |       auto &mac = it.bssid; | ||||||
|       ESP_LOGV(TAG, "AP client connected MAC=%s", format_mac_addr(mac).c_str()); |       ESP_LOGV(TAG, "AP client connected MAC=%s", format_mac_address_pretty(mac).c_str()); | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|     case ESPHOME_EVENT_ID_WIFI_AP_STADISCONNECTED: { |     case ESPHOME_EVENT_ID_WIFI_AP_STADISCONNECTED: { | ||||||
|       auto it = info.wifi_sta_disconnected; |       auto it = info.wifi_sta_disconnected; | ||||||
|       auto &mac = it.bssid; |       auto &mac = it.bssid; | ||||||
|       ESP_LOGV(TAG, "AP client disconnected MAC=%s", format_mac_addr(mac).c_str()); |       ESP_LOGV(TAG, "AP client disconnected MAC=%s", format_mac_address_pretty(mac).c_str()); | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|     case ESPHOME_EVENT_ID_WIFI_AP_STAIPASSIGNED: { |     case ESPHOME_EVENT_ID_WIFI_AP_STAIPASSIGNED: { | ||||||
| @@ -651,7 +651,7 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ | |||||||
|     } |     } | ||||||
|     case ESPHOME_EVENT_ID_WIFI_AP_PROBEREQRECVED: { |     case ESPHOME_EVENT_ID_WIFI_AP_PROBEREQRECVED: { | ||||||
|       auto it = info.wifi_ap_probereqrecved; |       auto it = info.wifi_ap_probereqrecved; | ||||||
|       ESP_LOGVV(TAG, "AP receive Probe Request MAC=%s RSSI=%d", format_mac_addr(it.mac).c_str(), it.rssi); |       ESP_LOGVV(TAG, "AP receive Probe Request MAC=%s RSSI=%d", format_mac_address_pretty(it.mac).c_str(), it.rssi); | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|     default: |     default: | ||||||
|   | |||||||
| @@ -496,7 +496,8 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) { | |||||||
|       char buf[33]; |       char buf[33]; | ||||||
|       memcpy(buf, it.ssid, it.ssid_len); |       memcpy(buf, it.ssid, it.ssid_len); | ||||||
|       buf[it.ssid_len] = '\0'; |       buf[it.ssid_len] = '\0'; | ||||||
|       ESP_LOGV(TAG, "Connected ssid='%s' bssid=%s channel=%u", buf, format_mac_addr(it.bssid).c_str(), it.channel); |       ESP_LOGV(TAG, "Connected ssid='%s' bssid=%s channel=%u", buf, format_mac_address_pretty(it.bssid).c_str(), | ||||||
|  |                it.channel); | ||||||
|       s_sta_connected = true; |       s_sta_connected = true; | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
| @@ -510,7 +511,7 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) { | |||||||
|         s_sta_connect_not_found = true; |         s_sta_connect_not_found = true; | ||||||
|       } else { |       } else { | ||||||
|         ESP_LOGW(TAG, "Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf, |         ESP_LOGW(TAG, "Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf, | ||||||
|                  format_mac_addr(it.bssid).c_str(), LOG_STR_ARG(get_disconnect_reason_str(it.reason))); |                  format_mac_address_pretty(it.bssid).c_str(), LOG_STR_ARG(get_disconnect_reason_str(it.reason))); | ||||||
|         s_sta_connect_error = true; |         s_sta_connect_error = true; | ||||||
|       } |       } | ||||||
|       s_sta_connected = false; |       s_sta_connected = false; | ||||||
| @@ -545,17 +546,17 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) { | |||||||
|     } |     } | ||||||
|     case EVENT_SOFTAPMODE_STACONNECTED: { |     case EVENT_SOFTAPMODE_STACONNECTED: { | ||||||
|       auto it = event->event_info.sta_connected; |       auto it = event->event_info.sta_connected; | ||||||
|       ESP_LOGV(TAG, "AP client connected MAC=%s aid=%u", format_mac_addr(it.mac).c_str(), it.aid); |       ESP_LOGV(TAG, "AP client connected MAC=%s aid=%u", format_mac_address_pretty(it.mac).c_str(), it.aid); | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|     case EVENT_SOFTAPMODE_STADISCONNECTED: { |     case EVENT_SOFTAPMODE_STADISCONNECTED: { | ||||||
|       auto it = event->event_info.sta_disconnected; |       auto it = event->event_info.sta_disconnected; | ||||||
|       ESP_LOGV(TAG, "AP client disconnected MAC=%s aid=%u", format_mac_addr(it.mac).c_str(), it.aid); |       ESP_LOGV(TAG, "AP client disconnected MAC=%s aid=%u", format_mac_address_pretty(it.mac).c_str(), it.aid); | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|     case EVENT_SOFTAPMODE_PROBEREQRECVED: { |     case EVENT_SOFTAPMODE_PROBEREQRECVED: { | ||||||
|       auto it = event->event_info.ap_probereqrecved; |       auto it = event->event_info.ap_probereqrecved; | ||||||
|       ESP_LOGVV(TAG, "AP receive Probe Request MAC=%s RSSI=%d", format_mac_addr(it.mac).c_str(), it.rssi); |       ESP_LOGVV(TAG, "AP receive Probe Request MAC=%s RSSI=%d", format_mac_address_pretty(it.mac).c_str(), it.rssi); | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
| #if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 4, 0) | #if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 4, 0) | ||||||
| @@ -567,7 +568,7 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) { | |||||||
|     } |     } | ||||||
|     case EVENT_SOFTAPMODE_DISTRIBUTE_STA_IP: { |     case EVENT_SOFTAPMODE_DISTRIBUTE_STA_IP: { | ||||||
|       auto it = event->event_info.distribute_sta_ip; |       auto it = event->event_info.distribute_sta_ip; | ||||||
|       ESP_LOGV(TAG, "AP Distribute Station IP MAC=%s IP=%s aid=%u", format_mac_addr(it.mac).c_str(), |       ESP_LOGV(TAG, "AP Distribute Station IP MAC=%s IP=%s aid=%u", format_mac_address_pretty(it.mac).c_str(), | ||||||
|                format_ip_addr(it.ip).c_str(), it.aid); |                format_ip_addr(it.ip).c_str(), it.aid); | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -691,7 +691,7 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) { | |||||||
|     memcpy(buf, it.ssid, it.ssid_len); |     memcpy(buf, it.ssid, it.ssid_len); | ||||||
|     buf[it.ssid_len] = '\0'; |     buf[it.ssid_len] = '\0'; | ||||||
|     ESP_LOGV(TAG, "Connected ssid='%s' bssid=" LOG_SECRET("%s") " channel=%u, authmode=%s", buf, |     ESP_LOGV(TAG, "Connected ssid='%s' bssid=" LOG_SECRET("%s") " channel=%u, authmode=%s", buf, | ||||||
|              format_mac_addr(it.bssid).c_str(), it.channel, get_auth_mode_str(it.authmode)); |              format_mac_address_pretty(it.bssid).c_str(), it.channel, get_auth_mode_str(it.authmode)); | ||||||
|     s_sta_connected = true; |     s_sta_connected = true; | ||||||
|  |  | ||||||
|   } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_DISCONNECTED) { |   } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_DISCONNECTED) { | ||||||
| @@ -708,7 +708,7 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) { | |||||||
|       return; |       return; | ||||||
|     } else { |     } else { | ||||||
|       ESP_LOGW(TAG, "Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf, |       ESP_LOGW(TAG, "Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf, | ||||||
|                format_mac_addr(it.bssid).c_str(), get_disconnect_reason_str(it.reason)); |                format_mac_address_pretty(it.bssid).c_str(), get_disconnect_reason_str(it.reason)); | ||||||
|       s_sta_connect_error = true; |       s_sta_connect_error = true; | ||||||
|     } |     } | ||||||
|     s_sta_connected = false; |     s_sta_connected = false; | ||||||
| @@ -780,15 +780,15 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) { | |||||||
|  |  | ||||||
|   } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_PROBEREQRECVED) { |   } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_PROBEREQRECVED) { | ||||||
|     const auto &it = data->data.ap_probe_req_rx; |     const auto &it = data->data.ap_probe_req_rx; | ||||||
|     ESP_LOGVV(TAG, "AP receive Probe Request MAC=%s RSSI=%d", format_mac_addr(it.mac).c_str(), it.rssi); |     ESP_LOGVV(TAG, "AP receive Probe Request MAC=%s RSSI=%d", format_mac_address_pretty(it.mac).c_str(), it.rssi); | ||||||
|  |  | ||||||
|   } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_STACONNECTED) { |   } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_STACONNECTED) { | ||||||
|     const auto &it = data->data.ap_staconnected; |     const auto &it = data->data.ap_staconnected; | ||||||
|     ESP_LOGV(TAG, "AP client connected MAC=%s", format_mac_addr(it.mac).c_str()); |     ESP_LOGV(TAG, "AP client connected MAC=%s", format_mac_address_pretty(it.mac).c_str()); | ||||||
|  |  | ||||||
|   } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_STADISCONNECTED) { |   } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_STADISCONNECTED) { | ||||||
|     const auto &it = data->data.ap_stadisconnected; |     const auto &it = data->data.ap_stadisconnected; | ||||||
|     ESP_LOGV(TAG, "AP client disconnected MAC=%s", format_mac_addr(it.mac).c_str()); |     ESP_LOGV(TAG, "AP client disconnected MAC=%s", format_mac_address_pretty(it.mac).c_str()); | ||||||
|  |  | ||||||
|   } else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_AP_STAIPASSIGNED) { |   } else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_AP_STAIPASSIGNED) { | ||||||
|     const auto &it = data->data.ip_ap_staipassigned; |     const auto &it = data->data.ip_ap_staipassigned; | ||||||
|   | |||||||
| @@ -281,7 +281,7 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ | |||||||
|       memcpy(buf, it.ssid, it.ssid_len); |       memcpy(buf, it.ssid, it.ssid_len); | ||||||
|       buf[it.ssid_len] = '\0'; |       buf[it.ssid_len] = '\0'; | ||||||
|       ESP_LOGV(TAG, "Connected ssid='%s' bssid=" LOG_SECRET("%s") " channel=%u, authmode=%s", buf, |       ESP_LOGV(TAG, "Connected ssid='%s' bssid=" LOG_SECRET("%s") " channel=%u, authmode=%s", buf, | ||||||
|                format_mac_addr(it.bssid).c_str(), it.channel, get_auth_mode_str(it.authmode)); |                format_mac_address_pretty(it.bssid).c_str(), it.channel, get_auth_mode_str(it.authmode)); | ||||||
|  |  | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
| @@ -294,7 +294,7 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ | |||||||
|         ESP_LOGW(TAG, "Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf); |         ESP_LOGW(TAG, "Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf); | ||||||
|       } else { |       } else { | ||||||
|         ESP_LOGW(TAG, "Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf, |         ESP_LOGW(TAG, "Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf, | ||||||
|                  format_mac_addr(it.bssid).c_str(), get_disconnect_reason_str(it.reason)); |                  format_mac_address_pretty(it.bssid).c_str(), get_disconnect_reason_str(it.reason)); | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       uint8_t reason = it.reason; |       uint8_t reason = it.reason; | ||||||
| @@ -349,13 +349,13 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ | |||||||
|     case ESPHOME_EVENT_ID_WIFI_AP_STACONNECTED: { |     case ESPHOME_EVENT_ID_WIFI_AP_STACONNECTED: { | ||||||
|       auto it = info.wifi_sta_connected; |       auto it = info.wifi_sta_connected; | ||||||
|       auto &mac = it.bssid; |       auto &mac = it.bssid; | ||||||
|       ESP_LOGV(TAG, "AP client connected MAC=%s", format_mac_addr(mac).c_str()); |       ESP_LOGV(TAG, "AP client connected MAC=%s", format_mac_address_pretty(mac).c_str()); | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|     case ESPHOME_EVENT_ID_WIFI_AP_STADISCONNECTED: { |     case ESPHOME_EVENT_ID_WIFI_AP_STADISCONNECTED: { | ||||||
|       auto it = info.wifi_sta_disconnected; |       auto it = info.wifi_sta_disconnected; | ||||||
|       auto &mac = it.bssid; |       auto &mac = it.bssid; | ||||||
|       ESP_LOGV(TAG, "AP client disconnected MAC=%s", format_mac_addr(mac).c_str()); |       ESP_LOGV(TAG, "AP client disconnected MAC=%s", format_mac_address_pretty(mac).c_str()); | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|     case ESPHOME_EVENT_ID_WIFI_AP_STAIPASSIGNED: { |     case ESPHOME_EVENT_ID_WIFI_AP_STAIPASSIGNED: { | ||||||
| @@ -364,7 +364,7 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ | |||||||
|     } |     } | ||||||
|     case ESPHOME_EVENT_ID_WIFI_AP_PROBEREQRECVED: { |     case ESPHOME_EVENT_ID_WIFI_AP_PROBEREQRECVED: { | ||||||
|       auto it = info.wifi_ap_probereqrecved; |       auto it = info.wifi_ap_probereqrecved; | ||||||
|       ESP_LOGVV(TAG, "AP receive Probe Request MAC=%s RSSI=%d", format_mac_addr(it.mac).c_str(), it.rssi); |       ESP_LOGVV(TAG, "AP receive Probe Request MAC=%s RSSI=%d", format_mac_address_pretty(it.mac).c_str(), it.rssi); | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|     default: |     default: | ||||||
|   | |||||||
| @@ -320,7 +320,7 @@ bool decrypt_xiaomi_payload(std::vector<uint8_t> &raw, const uint8_t *bindkey, c | |||||||
|     memcpy(mac_address + 4, mac_reverse + 1, 1); |     memcpy(mac_address + 4, mac_reverse + 1, 1); | ||||||
|     memcpy(mac_address + 5, mac_reverse, 1); |     memcpy(mac_address + 5, mac_reverse, 1); | ||||||
|     ESP_LOGVV(TAG, "decrypt_xiaomi_payload(): authenticated decryption failed."); |     ESP_LOGVV(TAG, "decrypt_xiaomi_payload(): authenticated decryption failed."); | ||||||
|     ESP_LOGVV(TAG, "  MAC address : %s", format_hex_pretty(mac_address, 6).c_str()); |     ESP_LOGVV(TAG, "  MAC address : %s", format_mac_address_pretty(mac_address).c_str()); | ||||||
|     ESP_LOGVV(TAG, "       Packet : %s", format_hex_pretty(raw.data(), raw.size()).c_str()); |     ESP_LOGVV(TAG, "       Packet : %s", format_hex_pretty(raw.data(), raw.size()).c_str()); | ||||||
|     ESP_LOGVV(TAG, "          Key : %s", format_hex_pretty(vector.key, vector.keysize).c_str()); |     ESP_LOGVV(TAG, "          Key : %s", format_hex_pretty(vector.key, vector.keysize).c_str()); | ||||||
|     ESP_LOGVV(TAG, "           Iv : %s", format_hex_pretty(vector.iv, vector.ivsize).c_str()); |     ESP_LOGVV(TAG, "           Iv : %s", format_hex_pretty(vector.iv, vector.ivsize).c_str()); | ||||||
|   | |||||||
| @@ -1099,7 +1099,7 @@ UNIT_KILOMETER_PER_HOUR = "km/h" | |||||||
| UNIT_KILOVOLT_AMPS = "kVA" | UNIT_KILOVOLT_AMPS = "kVA" | ||||||
| UNIT_KILOVOLT_AMPS_HOURS = "kVAh" | UNIT_KILOVOLT_AMPS_HOURS = "kVAh" | ||||||
| UNIT_KILOVOLT_AMPS_REACTIVE = "kVAR" | UNIT_KILOVOLT_AMPS_REACTIVE = "kVAR" | ||||||
| UNIT_KILOVOLT_AMPS_REACTIVE_HOURS = "kVARh" | UNIT_KILOVOLT_AMPS_REACTIVE_HOURS = "kvarh" | ||||||
| UNIT_KILOWATT = "kW" | UNIT_KILOWATT = "kW" | ||||||
| UNIT_KILOWATT_HOURS = "kWh" | UNIT_KILOWATT_HOURS = "kWh" | ||||||
| UNIT_LITRE = "L" | UNIT_LITRE = "L" | ||||||
| @@ -1135,7 +1135,7 @@ UNIT_VOLT = "V" | |||||||
| UNIT_VOLT_AMPS = "VA" | UNIT_VOLT_AMPS = "VA" | ||||||
| UNIT_VOLT_AMPS_HOURS = "VAh" | UNIT_VOLT_AMPS_HOURS = "VAh" | ||||||
| UNIT_VOLT_AMPS_REACTIVE = "var" | UNIT_VOLT_AMPS_REACTIVE = "var" | ||||||
| UNIT_VOLT_AMPS_REACTIVE_HOURS = "VARh" | UNIT_VOLT_AMPS_REACTIVE_HOURS = "varh" | ||||||
| UNIT_WATT = "W" | UNIT_WATT = "W" | ||||||
| UNIT_WATT_HOURS = "Wh" | UNIT_WATT_HOURS = "Wh" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -356,6 +356,10 @@ size_t parse_hex(const char *str, size_t length, uint8_t *data, size_t count) { | |||||||
|   return chars; |   return chars; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | std::string format_mac_address_pretty(const uint8_t *mac) { | ||||||
|  |   return str_snprintf("%02X:%02X:%02X:%02X:%02X:%02X", 17, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); | ||||||
|  | } | ||||||
|  |  | ||||||
| static char format_hex_char(uint8_t v) { return v >= 10 ? 'a' + (v - 10) : '0' + v; } | static char format_hex_char(uint8_t v) { return v >= 10 ? 'a' + (v - 10) : '0' + v; } | ||||||
| std::string format_hex(const uint8_t *data, size_t length) { | std::string format_hex(const uint8_t *data, size_t length) { | ||||||
|   std::string ret; |   std::string ret; | ||||||
| @@ -732,7 +736,7 @@ std::string get_mac_address() { | |||||||
| std::string get_mac_address_pretty() { | std::string get_mac_address_pretty() { | ||||||
|   uint8_t mac[6]; |   uint8_t mac[6]; | ||||||
|   get_mac_address_raw(mac); |   get_mac_address_raw(mac); | ||||||
|   return str_snprintf("%02X:%02X:%02X:%02X:%02X:%02X", 17, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); |   return format_mac_address_pretty(mac); | ||||||
| } | } | ||||||
|  |  | ||||||
| #ifdef USE_ESP32 | #ifdef USE_ESP32 | ||||||
|   | |||||||
| @@ -402,6 +402,8 @@ template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> optional< | |||||||
|   return parse_hex<T>(str.c_str(), str.length()); |   return parse_hex<T>(str.c_str(), str.length()); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /// Format the six-byte array \p mac into a MAC address. | ||||||
|  | std::string format_mac_address_pretty(const uint8_t mac[6]); | ||||||
| /// Format the byte array \p data of length \p len in lowercased hex. | /// Format the byte array \p data of length \p len in lowercased hex. | ||||||
| std::string format_hex(const uint8_t *data, size_t length); | std::string format_hex(const uint8_t *data, size_t length); | ||||||
| /// Format the vector \p data in lowercased hex. | /// Format the vector \p data in lowercased hex. | ||||||
|   | |||||||
| @@ -4,6 +4,31 @@ binary_sensor: | |||||||
|     id: some_binary_sensor |     id: some_binary_sensor | ||||||
|     name: "Random binary" |     name: "Random binary" | ||||||
|     lambda: return (random_uint32() & 1) == 0; |     lambda: return (random_uint32() & 1) == 0; | ||||||
|  |     filters: | ||||||
|  |       - invert: | ||||||
|  |       - delayed_on: 100ms | ||||||
|  |       - delayed_off: 100ms | ||||||
|  |       # Templated, delays for 1s (1000ms) only if a reed switch is active | ||||||
|  |       - delayed_on_off: !lambda "return 1000;" | ||||||
|  |       - delayed_on_off: | ||||||
|  |           time_on: 10s | ||||||
|  |           time_off: !lambda "return 1000;" | ||||||
|  |       - autorepeat: | ||||||
|  |           - delay: 1s | ||||||
|  |             time_off: 100ms | ||||||
|  |             time_on: 900ms | ||||||
|  |           - delay: 5s | ||||||
|  |             time_off: 100ms | ||||||
|  |             time_on: 400ms | ||||||
|  |       - lambda: |- | ||||||
|  |           if (id(some_binary_sensor).state) { | ||||||
|  |             return x; | ||||||
|  |           } else { | ||||||
|  |             return {}; | ||||||
|  |           } | ||||||
|  |       - settle: 100ms | ||||||
|  |       - timeout: 10s | ||||||
|  |  | ||||||
|     on_state_change: |     on_state_change: | ||||||
|       then: |       then: | ||||||
|         - logger.log: |         - logger.log: | ||||||
|   | |||||||
| @@ -8,5 +8,8 @@ sensor: | |||||||
|     name: Test Sensor |     name: Test Sensor | ||||||
|     id: test_sensor |     id: test_sensor | ||||||
|     unit_of_measurement: °C |     unit_of_measurement: °C | ||||||
|  |     accuracy_decimals: 2 | ||||||
|  |     state_class: measurement | ||||||
|  |     force_update: true | ||||||
|     lambda: return 42.0; |     lambda: return 42.0; | ||||||
|     update_interval: 0.1s |     update_interval: 0.1s | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ from __future__ import annotations | |||||||
|  |  | ||||||
| import asyncio | import asyncio | ||||||
|  |  | ||||||
|  | import aioesphomeapi | ||||||
| from aioesphomeapi import EntityState | from aioesphomeapi import EntityState | ||||||
| import pytest | import pytest | ||||||
|  |  | ||||||
| @@ -47,3 +48,23 @@ async def test_host_mode_with_sensor( | |||||||
|         # Verify the sensor state |         # Verify the sensor state | ||||||
|         assert test_sensor_state.state == 42.0 |         assert test_sensor_state.state == 42.0 | ||||||
|         assert len(states) > 0, "No states received" |         assert len(states) > 0, "No states received" | ||||||
|  |  | ||||||
|  |         # Verify the optimized fields are working correctly | ||||||
|  |         # Get entity info to check accuracy_decimals, state_class, etc. | ||||||
|  |         entities, _ = await client.list_entities_services() | ||||||
|  |         sensor_info: aioesphomeapi.SensorInfo | None = None | ||||||
|  |         for entity in entities: | ||||||
|  |             if isinstance(entity, aioesphomeapi.SensorInfo): | ||||||
|  |                 sensor_info = entity | ||||||
|  |                 break | ||||||
|  |  | ||||||
|  |         assert sensor_info is not None, "Sensor entity info not found" | ||||||
|  |         assert sensor_info.accuracy_decimals == 2, ( | ||||||
|  |             f"Expected accuracy_decimals=2, got {sensor_info.accuracy_decimals}" | ||||||
|  |         ) | ||||||
|  |         assert sensor_info.state_class == aioesphomeapi.StateClass.MEASUREMENT, ( | ||||||
|  |             f"Expected state_class=StateClass.MEASUREMENT, got {sensor_info.state_class}" | ||||||
|  |         ) | ||||||
|  |         assert sensor_info.force_update is True, ( | ||||||
|  |             f"Expected force_update=True, got {sensor_info.force_update}" | ||||||
|  |         ) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user