mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	[zwave_proxy] Fix race condition sending zero home ID on reboot (#10848)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
This commit is contained in:
		| @@ -1,4 +1,5 @@ | |||||||
| #include "zwave_proxy.h" | #include "zwave_proxy.h" | ||||||
|  | #include "esphome/core/application.h" | ||||||
| #include "esphome/core/helpers.h" | #include "esphome/core/helpers.h" | ||||||
| #include "esphome/core/log.h" | #include "esphome/core/log.h" | ||||||
| #include "esphome/core/util.h" | #include "esphome/core/util.h" | ||||||
| @@ -12,6 +13,7 @@ static constexpr uint8_t ZWAVE_COMMAND_GET_NETWORK_IDS = 0x20; | |||||||
| // GET_NETWORK_IDS response: [SOF][LENGTH][TYPE][CMD][HOME_ID(4)][NODE_ID][...] | // GET_NETWORK_IDS response: [SOF][LENGTH][TYPE][CMD][HOME_ID(4)][NODE_ID][...] | ||||||
| static constexpr uint8_t ZWAVE_COMMAND_TYPE_RESPONSE = 0x01;    // Response type field value | static constexpr uint8_t ZWAVE_COMMAND_TYPE_RESPONSE = 0x01;    // Response type field value | ||||||
| static constexpr uint8_t ZWAVE_MIN_GET_NETWORK_IDS_LENGTH = 9;  // TYPE + CMD + HOME_ID(4) + NODE_ID + checksum | static constexpr uint8_t ZWAVE_MIN_GET_NETWORK_IDS_LENGTH = 9;  // TYPE + CMD + HOME_ID(4) + NODE_ID + checksum | ||||||
|  | static constexpr uint32_t HOME_ID_TIMEOUT_MS = 100;             // Timeout for waiting for home ID during setup | ||||||
|  |  | ||||||
| static uint8_t calculate_frame_checksum(const uint8_t *data, uint8_t length) { | static uint8_t calculate_frame_checksum(const uint8_t *data, uint8_t length) { | ||||||
|   // Calculate Z-Wave frame checksum |   // Calculate Z-Wave frame checksum | ||||||
| @@ -26,7 +28,44 @@ static uint8_t calculate_frame_checksum(const uint8_t *data, uint8_t length) { | |||||||
|  |  | ||||||
| ZWaveProxy::ZWaveProxy() { global_zwave_proxy = this; } | ZWaveProxy::ZWaveProxy() { global_zwave_proxy = this; } | ||||||
|  |  | ||||||
| void ZWaveProxy::setup() { this->send_simple_command_(ZWAVE_COMMAND_GET_NETWORK_IDS); } | void ZWaveProxy::setup() { | ||||||
|  |   this->setup_time_ = App.get_loop_component_start_time(); | ||||||
|  |   this->send_simple_command_(ZWAVE_COMMAND_GET_NETWORK_IDS); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | float ZWaveProxy::get_setup_priority() const { | ||||||
|  |   // Set up before API so home ID is ready when API starts | ||||||
|  |   return setup_priority::BEFORE_CONNECTION; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool ZWaveProxy::can_proceed() { | ||||||
|  |   // If we already have the home ID, we can proceed | ||||||
|  |   if (this->home_id_ready_) { | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Handle any pending responses | ||||||
|  |   if (this->response_handler_()) { | ||||||
|  |     ESP_LOGV(TAG, "Handled response during setup"); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Process UART data to check for home ID | ||||||
|  |   this->process_uart_(); | ||||||
|  |  | ||||||
|  |   // Check if we got the home ID after processing | ||||||
|  |   if (this->home_id_ready_) { | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Wait up to HOME_ID_TIMEOUT_MS for home ID response | ||||||
|  |   const uint32_t now = App.get_loop_component_start_time(); | ||||||
|  |   if (now - this->setup_time_ > HOME_ID_TIMEOUT_MS) { | ||||||
|  |     ESP_LOGW(TAG, "Timeout reading Home ID during setup"); | ||||||
|  |     return true;  // Proceed anyway after timeout | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return false;  // Keep waiting | ||||||
|  | } | ||||||
|  |  | ||||||
| void ZWaveProxy::loop() { | void ZWaveProxy::loop() { | ||||||
|   if (this->response_handler_()) { |   if (this->response_handler_()) { | ||||||
| @@ -37,6 +76,11 @@ void ZWaveProxy::loop() { | |||||||
|     this->api_connection_ = nullptr;  // Unsubscribe if disconnected |     this->api_connection_ = nullptr;  // Unsubscribe if disconnected | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   this->process_uart_(); | ||||||
|  |   this->status_clear_warning(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ZWaveProxy::process_uart_() { | ||||||
|   while (this->available()) { |   while (this->available()) { | ||||||
|     uint8_t byte; |     uint8_t byte; | ||||||
|     if (!this->read_byte(&byte)) { |     if (!this->read_byte(&byte)) { | ||||||
| @@ -56,6 +100,7 @@ void ZWaveProxy::loop() { | |||||||
|         // Extract the 4-byte Home ID starting at offset 4 |         // Extract the 4-byte Home ID starting at offset 4 | ||||||
|         // The frame parser has already validated the checksum and ensured all bytes are present |         // The frame parser has already validated the checksum and ensured all bytes are present | ||||||
|         std::memcpy(this->home_id_.data(), this->buffer_.data() + 4, this->home_id_.size()); |         std::memcpy(this->home_id_.data(), this->buffer_.data() + 4, this->home_id_.size()); | ||||||
|  |         this->home_id_ready_ = true; | ||||||
|         ESP_LOGI(TAG, "Home ID: %s", |         ESP_LOGI(TAG, "Home ID: %s", | ||||||
|                  format_hex_pretty(this->home_id_.data(), this->home_id_.size(), ':', false).c_str()); |                  format_hex_pretty(this->home_id_.data(), this->home_id_.size(), ':', false).c_str()); | ||||||
|       } |       } | ||||||
| @@ -73,7 +118,6 @@ void ZWaveProxy::loop() { | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   this->status_clear_warning(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void ZWaveProxy::dump_config() { ESP_LOGCONFIG(TAG, "Z-Wave Proxy"); } | void ZWaveProxy::dump_config() { ESP_LOGCONFIG(TAG, "Z-Wave Proxy"); } | ||||||
|   | |||||||
| @@ -44,6 +44,8 @@ class ZWaveProxy : public uart::UARTDevice, public Component { | |||||||
|   void setup() override; |   void setup() override; | ||||||
|   void loop() override; |   void loop() override; | ||||||
|   void dump_config() override; |   void dump_config() override; | ||||||
|  |   float get_setup_priority() const override; | ||||||
|  |   bool can_proceed() override; | ||||||
|  |  | ||||||
|   void zwave_proxy_request(api::APIConnection *api_connection, api::enums::ZWaveProxyRequestType type); |   void zwave_proxy_request(api::APIConnection *api_connection, api::enums::ZWaveProxyRequestType type); | ||||||
|   api::APIConnection *get_api_connection() { return this->api_connection_; } |   api::APIConnection *get_api_connection() { return this->api_connection_; } | ||||||
| @@ -60,19 +62,24 @@ class ZWaveProxy : public uart::UARTDevice, public Component { | |||||||
|   bool parse_byte_(uint8_t byte);  // Returns true if frame parsing was completed (a frame is ready in the buffer) |   bool parse_byte_(uint8_t byte);  // Returns true if frame parsing was completed (a frame is ready in the buffer) | ||||||
|   void parse_start_(uint8_t byte); |   void parse_start_(uint8_t byte); | ||||||
|   bool response_handler_(); |   bool response_handler_(); | ||||||
|  |   void process_uart_();  // Process all available UART data | ||||||
|  |  | ||||||
|   api::APIConnection *api_connection_{nullptr};  // Current subscribed client |   // Pre-allocated message - always ready to send | ||||||
|  |   api::ZWaveProxyFrame outgoing_proto_msg_; | ||||||
|   std::array<uint8_t, 4> home_id_{0, 0, 0, 0};                      // Fixed buffer for home ID |  | ||||||
|   std::array<uint8_t, sizeof(api::ZWaveProxyFrame::data)> buffer_;  // Fixed buffer for incoming data |   std::array<uint8_t, sizeof(api::ZWaveProxyFrame::data)> buffer_;  // Fixed buffer for incoming data | ||||||
|  |   std::array<uint8_t, 4> home_id_{0, 0, 0, 0};                      // Fixed buffer for home ID | ||||||
|  |  | ||||||
|  |   // Pointers and 32-bit values (aligned together) | ||||||
|  |   api::APIConnection *api_connection_{nullptr};  // Current subscribed client | ||||||
|  |   uint32_t setup_time_{0};                       // Time when setup() was called | ||||||
|  |  | ||||||
|  |   // 8-bit values (grouped together to minimize padding) | ||||||
|   uint8_t buffer_index_{0};     // Index for populating the data buffer |   uint8_t buffer_index_{0};     // Index for populating the data buffer | ||||||
|   uint8_t end_frame_after_{0};  // Payload reception ends after this index |   uint8_t end_frame_after_{0};  // Payload reception ends after this index | ||||||
|   uint8_t last_response_{0};    // Last response type sent |   uint8_t last_response_{0};    // Last response type sent | ||||||
|   ZWaveParsingState parsing_state_{ZWAVE_PARSING_STATE_WAIT_START}; |   ZWaveParsingState parsing_state_{ZWAVE_PARSING_STATE_WAIT_START}; | ||||||
|   bool in_bootloader_{false};  // True if the device is detected to be in bootloader mode |   bool in_bootloader_{false};  // True if the device is detected to be in bootloader mode | ||||||
|  |   bool home_id_ready_{false};  // True when home ID has been received from Z-Wave module | ||||||
|   // Pre-allocated message - always ready to send |  | ||||||
|   api::ZWaveProxyFrame outgoing_proto_msg_; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| extern ZWaveProxy *global_zwave_proxy;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) | extern ZWaveProxy *global_zwave_proxy;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user