mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	Merge branch 'integration' into memory_api
This commit is contained in:
		
							
								
								
									
										24
									
								
								.github/workflows/needs-docs.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										24
									
								
								.github/workflows/needs-docs.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,24 +0,0 @@ | |||||||
| name: Needs Docs |  | ||||||
|  |  | ||||||
| on: |  | ||||||
|   pull_request: |  | ||||||
|     types: [labeled, unlabeled] |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   check: |  | ||||||
|     name: Check |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|     steps: |  | ||||||
|       - name: Check for needs-docs label |  | ||||||
|         uses: actions/github-script@v7.0.1 |  | ||||||
|         with: |  | ||||||
|           script: | |  | ||||||
|             const { data: labels } = await github.rest.issues.listLabelsOnIssue({ |  | ||||||
|               owner: context.repo.owner, |  | ||||||
|               repo: context.repo.repo, |  | ||||||
|               issue_number: context.issue.number |  | ||||||
|             }); |  | ||||||
|             const needsDocs = labels.find(label => label.name === 'needs-docs'); |  | ||||||
|             if (needsDocs) { |  | ||||||
|               core.setFailed('Pull request needs docs'); |  | ||||||
|             } |  | ||||||
							
								
								
									
										30
									
								
								.github/workflows/status-check-labels.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								.github/workflows/status-check-labels.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | name: Status check labels | ||||||
|  |  | ||||||
|  | on: | ||||||
|  |   pull_request: | ||||||
|  |     types: [labeled, unlabeled] | ||||||
|  |  | ||||||
|  | jobs: | ||||||
|  |   check: | ||||||
|  |     name: Check ${{ matrix.label }} | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     strategy: | ||||||
|  |       fail-fast: false | ||||||
|  |       matrix: | ||||||
|  |         label: | ||||||
|  |           - needs-docs | ||||||
|  |           - merge-after-release | ||||||
|  |     steps: | ||||||
|  |       - name: Check for ${{ matrix.label }} label | ||||||
|  |         uses: actions/github-script@v7.0.1 | ||||||
|  |         with: | ||||||
|  |           script: | | ||||||
|  |             const { data: labels } = await github.rest.issues.listLabelsOnIssue({ | ||||||
|  |               owner: context.repo.owner, | ||||||
|  |               repo: context.repo.repo, | ||||||
|  |               issue_number: context.issue.number | ||||||
|  |             }); | ||||||
|  |             const hasLabel = labels.find(label => label.name === '${{ matrix.label }}'); | ||||||
|  |             if (hasLabel) { | ||||||
|  |               core.setFailed('Pull request cannot be merged, it is labeled as ${{ matrix.label }}'); | ||||||
|  |             } | ||||||
| @@ -382,20 +382,15 @@ float ATM90E32Component::get_setup_priority() const { return setup_priority::IO; | |||||||
| // R/C registers can conly be cleared after the LastSPIData register is updated (register 78H) | // R/C registers can conly be cleared after the LastSPIData register is updated (register 78H) | ||||||
| // Peakdetect period: 05H. Bit 15:8 are PeakDet_period in ms. 7:0 are Sag_period | // Peakdetect period: 05H. Bit 15:8 are PeakDet_period in ms. 7:0 are Sag_period | ||||||
| // Default is 143FH (20ms, 63ms) | // Default is 143FH (20ms, 63ms) | ||||||
| uint16_t ATM90E32Component::read16_transaction_(uint16_t a_register) { | uint16_t ATM90E32Component::read16_(uint16_t a_register) { | ||||||
|  |   this->enable(); | ||||||
|  |   delay_microseconds_safe(1);  // min delay between CS low and first SCK is 200ns - 1us is plenty | ||||||
|   uint8_t addrh = (1 << 7) | ((a_register >> 8) & 0x03); |   uint8_t addrh = (1 << 7) | ((a_register >> 8) & 0x03); | ||||||
|   uint8_t addrl = (a_register & 0xFF); |   uint8_t addrl = (a_register & 0xFF); | ||||||
|   uint8_t data[4] = {addrh, addrl, 0x00, 0x00}; |   uint8_t data[4] = {addrh, addrl, 0x00, 0x00}; | ||||||
|   this->transfer_array(data, 4); |   this->transfer_array(data, 4); | ||||||
|   uint16_t output = encode_uint16(data[2], data[3]); |   uint16_t output = encode_uint16(data[2], data[3]); | ||||||
|   ESP_LOGVV(TAG, "read16_ 0x%04" PRIX16 " output 0x%04" PRIX16, a_register, output); |   ESP_LOGVV(TAG, "read16_ 0x%04" PRIX16 " output 0x%04" PRIX16, a_register, output); | ||||||
|   return output; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| uint16_t ATM90E32Component::read16_(uint16_t a_register) { |  | ||||||
|   this->enable(); |  | ||||||
|   delay_microseconds_safe(1);  // min delay between CS low and first SCK is 200ns - 1us is plenty |  | ||||||
|   uint16_t output = this->read16_transaction_(a_register); |  | ||||||
|   delay_microseconds_safe(1);  // allow the last clock to propagate before releasing CS |   delay_microseconds_safe(1);  // allow the last clock to propagate before releasing CS | ||||||
|   this->disable(); |   this->disable(); | ||||||
|   delay_microseconds_safe(1);  // meet minimum CS high time before next transaction |   delay_microseconds_safe(1);  // meet minimum CS high time before next transaction | ||||||
| @@ -403,14 +398,8 @@ uint16_t ATM90E32Component::read16_(uint16_t a_register) { | |||||||
| } | } | ||||||
|  |  | ||||||
| int ATM90E32Component::read32_(uint16_t addr_h, uint16_t addr_l) { | int ATM90E32Component::read32_(uint16_t addr_h, uint16_t addr_l) { | ||||||
|   this->enable(); |   const uint16_t val_h = this->read16_(addr_h); | ||||||
|   delay_microseconds_safe(1); |   const uint16_t val_l = this->read16_(addr_l); | ||||||
|   const uint16_t val_h = this->read16_transaction_(addr_h); |  | ||||||
|   delay_microseconds_safe(1); |  | ||||||
|   const uint16_t val_l = this->read16_transaction_(addr_l); |  | ||||||
|   delay_microseconds_safe(1); |  | ||||||
|   this->disable(); |  | ||||||
|   delay_microseconds_safe(1); |  | ||||||
|   const int32_t val = (val_h << 16) | val_l; |   const int32_t val = (val_h << 16) | val_l; | ||||||
|  |  | ||||||
|   ESP_LOGVV(TAG, |   ESP_LOGVV(TAG, | ||||||
|   | |||||||
| @@ -140,7 +140,6 @@ class ATM90E32Component : public PollingComponent, | |||||||
|   number::Number *ref_currents_[3]{nullptr, nullptr, nullptr}; |   number::Number *ref_currents_[3]{nullptr, nullptr, nullptr}; | ||||||
| #endif | #endif | ||||||
|   uint16_t read16_(uint16_t a_register); |   uint16_t read16_(uint16_t a_register); | ||||||
|   uint16_t read16_transaction_(uint16_t a_register); |  | ||||||
|   int read32_(uint16_t addr_h, uint16_t addr_l); |   int read32_(uint16_t addr_h, uint16_t addr_l); | ||||||
|   void write16_(uint16_t a_register, uint16_t val, bool validate = true); |   void write16_(uint16_t a_register, uint16_t val, bool validate = true); | ||||||
|   float get_local_phase_voltage_(uint8_t phase); |   float get_local_phase_voltage_(uint8_t phase); | ||||||
|   | |||||||
| @@ -375,10 +375,19 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga | |||||||
|  |  | ||||||
|   switch (event) { |   switch (event) { | ||||||
|     case ESP_GATTC_DISCONNECT_EVT: { |     case ESP_GATTC_DISCONNECT_EVT: { | ||||||
|       this->reset_connection_(param->disconnect.reason); |       // Don't reset connection yet - wait for CLOSE_EVT to ensure controller has freed resources | ||||||
|  |       // This prevents race condition where we mark slot as free before controller cleanup is complete | ||||||
|  |       ESP_LOGD(TAG, "[%d] [%s] Disconnect, reason=0x%02x", this->connection_index_, this->address_str_.c_str(), | ||||||
|  |                param->disconnect.reason); | ||||||
|  |       // Send disconnection notification but don't free the slot yet | ||||||
|  |       this->proxy_->send_device_connection(this->address_, false, 0, param->disconnect.reason); | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|     case ESP_GATTC_CLOSE_EVT: { |     case ESP_GATTC_CLOSE_EVT: { | ||||||
|  |       ESP_LOGD(TAG, "[%d] [%s] Close, reason=0x%02x, freeing slot", this->connection_index_, this->address_str_.c_str(), | ||||||
|  |                param->close.reason); | ||||||
|  |       // Now the GATT connection is fully closed and controller resources are freed | ||||||
|  |       // Safe to mark the connection slot as available | ||||||
|       this->reset_connection_(param->close.reason); |       this->reset_connection_(param->close.reason); | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -824,8 +824,9 @@ async def to_code(config): | |||||||
|     cg.set_cpp_standard("gnu++20") |     cg.set_cpp_standard("gnu++20") | ||||||
|     cg.add_build_flag("-DUSE_ESP32") |     cg.add_build_flag("-DUSE_ESP32") | ||||||
|     cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) |     cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) | ||||||
|     cg.add_build_flag(f"-DUSE_ESP32_VARIANT_{config[CONF_VARIANT]}") |     variant = config[CONF_VARIANT] | ||||||
|     cg.add_define("ESPHOME_VARIANT", VARIANT_FRIENDLY[config[CONF_VARIANT]]) |     cg.add_build_flag(f"-DUSE_ESP32_VARIANT_{variant}") | ||||||
|  |     cg.add_define("ESPHOME_VARIANT", VARIANT_FRIENDLY[variant]) | ||||||
|     cg.add_define(ThreadModel.MULTI_ATOMICS) |     cg.add_define(ThreadModel.MULTI_ATOMICS) | ||||||
|  |  | ||||||
|     cg.add_platformio_option("lib_ldf_mode", "off") |     cg.add_platformio_option("lib_ldf_mode", "off") | ||||||
| @@ -859,6 +860,7 @@ async def to_code(config): | |||||||
|         cg.add_platformio_option( |         cg.add_platformio_option( | ||||||
|             "platform_packages", ["espressif/toolchain-esp32ulp@2.35.0-20220830"] |             "platform_packages", ["espressif/toolchain-esp32ulp@2.35.0-20220830"] | ||||||
|         ) |         ) | ||||||
|  |         add_idf_sdkconfig_option(f"CONFIG_IDF_TARGET_{variant}", True) | ||||||
|         add_idf_sdkconfig_option( |         add_idf_sdkconfig_option( | ||||||
|             f"CONFIG_ESPTOOLPY_FLASHSIZE_{config[CONF_FLASH_SIZE]}", True |             f"CONFIG_ESPTOOLPY_FLASHSIZE_{config[CONF_FLASH_SIZE]}", True | ||||||
|         ) |         ) | ||||||
|   | |||||||
| @@ -764,7 +764,8 @@ void Nextion::process_nextion_commands_() { | |||||||
|         variable_name = to_process.substr(0, index); |         variable_name = to_process.substr(0, index); | ||||||
|         ++index; |         ++index; | ||||||
|  |  | ||||||
|         text_value = to_process.substr(index); |         // Get variable value without terminating NUL byte.  Length check above ensures substr len >= 0. | ||||||
|  |         text_value = to_process.substr(index, to_process_length - index - 1); | ||||||
|  |  | ||||||
|         ESP_LOGN(TAG, "Text sensor: %s='%s'", variable_name.c_str(), text_value.c_str()); |         ESP_LOGN(TAG, "Text sensor: %s='%s'", variable_name.c_str(), text_value.c_str()); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -23,20 +23,18 @@ void Pipsolar::loop() { | |||||||
|   // Read message |   // Read message | ||||||
|   if (this->state_ == STATE_IDLE) { |   if (this->state_ == STATE_IDLE) { | ||||||
|     this->empty_uart_buffer_(); |     this->empty_uart_buffer_(); | ||||||
|     switch (this->send_next_command_()) { |  | ||||||
|       case 0: |     if (this->send_next_command_()) { | ||||||
|         // no command send (empty queue) time to poll |       // command sent | ||||||
|         if (millis() - this->last_poll_ > this->update_interval_) { |  | ||||||
|           this->send_next_poll_(); |  | ||||||
|           this->last_poll_ = millis(); |  | ||||||
|         } |  | ||||||
|       return; |       return; | ||||||
|         break; |  | ||||||
|       case 1: |  | ||||||
|         // command send |  | ||||||
|         return; |  | ||||||
|         break; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (this->send_next_poll_()) { | ||||||
|  |       // poll sent | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return; | ||||||
|   } |   } | ||||||
|   if (this->state_ == STATE_COMMAND_COMPLETE) { |   if (this->state_ == STATE_COMMAND_COMPLETE) { | ||||||
|     if (this->check_incoming_length_(4)) { |     if (this->check_incoming_length_(4)) { | ||||||
| @@ -530,7 +528,7 @@ void Pipsolar::loop() { | |||||||
|         // '(00000000000000000000000000000000' |         // '(00000000000000000000000000000000' | ||||||
|         // iterate over all available flag (as not all models have all flags, but at least in the same order) |         // iterate over all available flag (as not all models have all flags, but at least in the same order) | ||||||
|         this->value_warnings_present_ = false; |         this->value_warnings_present_ = false; | ||||||
|         this->value_faults_present_ = true; |         this->value_faults_present_ = false; | ||||||
|  |  | ||||||
|         for (size_t i = 1; i < strlen(tmp); i++) { |         for (size_t i = 1; i < strlen(tmp); i++) { | ||||||
|           enabled = tmp[i] == '1'; |           enabled = tmp[i] == '1'; | ||||||
| @@ -708,6 +706,7 @@ void Pipsolar::loop() { | |||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
|       // crc ok |       // crc ok | ||||||
|  |       this->used_polling_commands_[this->last_polling_command_].needs_update = false; | ||||||
|       this->state_ = STATE_POLL_CHECKED; |       this->state_ = STATE_POLL_CHECKED; | ||||||
|       return; |       return; | ||||||
|     } else { |     } else { | ||||||
| @@ -788,7 +787,7 @@ uint8_t Pipsolar::check_incoming_crc_() { | |||||||
| } | } | ||||||
|  |  | ||||||
| // send next command used | // send next command used | ||||||
| uint8_t Pipsolar::send_next_command_() { | bool Pipsolar::send_next_command_() { | ||||||
|   uint16_t crc16; |   uint16_t crc16; | ||||||
|   if (!this->command_queue_[this->command_queue_position_].empty()) { |   if (!this->command_queue_[this->command_queue_position_].empty()) { | ||||||
|     const char *command = this->command_queue_[this->command_queue_position_].c_str(); |     const char *command = this->command_queue_[this->command_queue_position_].c_str(); | ||||||
| @@ -809,20 +808,23 @@ uint8_t Pipsolar::send_next_command_() { | |||||||
|     // end Byte |     // end Byte | ||||||
|     this->write(0x0D); |     this->write(0x0D); | ||||||
|     ESP_LOGD(TAG, "Sending command from queue: %s with length %d", command, length); |     ESP_LOGD(TAG, "Sending command from queue: %s with length %d", command, length); | ||||||
|     return 1; |     return true; | ||||||
|   } |   } | ||||||
|   return 0; |   return false; | ||||||
| } | } | ||||||
|  |  | ||||||
| void Pipsolar::send_next_poll_() { | bool Pipsolar::send_next_poll_() { | ||||||
|   uint16_t crc16; |   uint16_t crc16; | ||||||
|   this->last_polling_command_ = (this->last_polling_command_ + 1) % 15; |  | ||||||
|  |   for (uint8_t i = 0; i < POLLING_COMMANDS_MAX; i++) { | ||||||
|  |     this->last_polling_command_ = (this->last_polling_command_ + 1) % POLLING_COMMANDS_MAX; | ||||||
|     if (this->used_polling_commands_[this->last_polling_command_].length == 0) { |     if (this->used_polling_commands_[this->last_polling_command_].length == 0) { | ||||||
|     this->last_polling_command_ = 0; |       // not enabled | ||||||
|  |       continue; | ||||||
|     } |     } | ||||||
|   if (this->used_polling_commands_[this->last_polling_command_].length == 0) { |     if (!this->used_polling_commands_[this->last_polling_command_].needs_update) { | ||||||
|     // no command specified |       // no update requested | ||||||
|     return; |       continue; | ||||||
|     } |     } | ||||||
|     this->state_ = STATE_POLL; |     this->state_ = STATE_POLL; | ||||||
|     this->command_start_millis_ = millis(); |     this->command_start_millis_ = millis(); | ||||||
| @@ -840,6 +842,9 @@ void Pipsolar::send_next_poll_() { | |||||||
|     ESP_LOGD(TAG, "Sending polling command : %s with length %d", |     ESP_LOGD(TAG, "Sending polling command : %s with length %d", | ||||||
|              this->used_polling_commands_[this->last_polling_command_].command, |              this->used_polling_commands_[this->last_polling_command_].command, | ||||||
|              this->used_polling_commands_[this->last_polling_command_].length); |              this->used_polling_commands_[this->last_polling_command_].length); | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |   return false; | ||||||
| } | } | ||||||
|  |  | ||||||
| void Pipsolar::queue_command_(const char *command, uint8_t length) { | void Pipsolar::queue_command_(const char *command, uint8_t length) { | ||||||
| @@ -869,7 +874,13 @@ void Pipsolar::dump_config() { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
| void Pipsolar::update() {} | void Pipsolar::update() { | ||||||
|  |   for (auto &used_polling_command : this->used_polling_commands_) { | ||||||
|  |     if (used_polling_command.length != 0) { | ||||||
|  |       used_polling_command.needs_update = true; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
| void Pipsolar::add_polling_command_(const char *command, ENUMPollingCommand polling_command) { | void Pipsolar::add_polling_command_(const char *command, ENUMPollingCommand polling_command) { | ||||||
|   for (auto &used_polling_command : this->used_polling_commands_) { |   for (auto &used_polling_command : this->used_polling_commands_) { | ||||||
| @@ -891,6 +902,7 @@ void Pipsolar::add_polling_command_(const char *command, ENUMPollingCommand poll | |||||||
|       used_polling_command.errors = 0; |       used_polling_command.errors = 0; | ||||||
|       used_polling_command.identifier = polling_command; |       used_polling_command.identifier = polling_command; | ||||||
|       used_polling_command.length = length - 1; |       used_polling_command.length = length - 1; | ||||||
|  |       used_polling_command.needs_update = true; | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -25,6 +25,7 @@ struct PollingCommand { | |||||||
|   uint8_t length = 0; |   uint8_t length = 0; | ||||||
|   uint8_t errors; |   uint8_t errors; | ||||||
|   ENUMPollingCommand identifier; |   ENUMPollingCommand identifier; | ||||||
|  |   bool needs_update; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #define PIPSOLAR_VALUED_ENTITY_(type, name, polling_command, value_type) \ | #define PIPSOLAR_VALUED_ENTITY_(type, name, polling_command, value_type) \ | ||||||
| @@ -189,14 +190,14 @@ class Pipsolar : public uart::UARTDevice, public PollingComponent { | |||||||
|   static const size_t PIPSOLAR_READ_BUFFER_LENGTH = 110;  // maximum supported answer length |   static const size_t PIPSOLAR_READ_BUFFER_LENGTH = 110;  // maximum supported answer length | ||||||
|   static const size_t COMMAND_QUEUE_LENGTH = 10; |   static const size_t COMMAND_QUEUE_LENGTH = 10; | ||||||
|   static const size_t COMMAND_TIMEOUT = 5000; |   static const size_t COMMAND_TIMEOUT = 5000; | ||||||
|   uint32_t last_poll_ = 0; |   static const size_t POLLING_COMMANDS_MAX = 15; | ||||||
|   void add_polling_command_(const char *command, ENUMPollingCommand polling_command); |   void add_polling_command_(const char *command, ENUMPollingCommand polling_command); | ||||||
|   void empty_uart_buffer_(); |   void empty_uart_buffer_(); | ||||||
|   uint8_t check_incoming_crc_(); |   uint8_t check_incoming_crc_(); | ||||||
|   uint8_t check_incoming_length_(uint8_t length); |   uint8_t check_incoming_length_(uint8_t length); | ||||||
|   uint16_t pipsolar_crc_(uint8_t *msg, uint8_t len); |   uint16_t pipsolar_crc_(uint8_t *msg, uint8_t len); | ||||||
|   uint8_t send_next_command_(); |   bool send_next_command_(); | ||||||
|   void send_next_poll_(); |   bool send_next_poll_(); | ||||||
|   void queue_command_(const char *command, uint8_t length); |   void queue_command_(const char *command, uint8_t length); | ||||||
|   std::string command_queue_[COMMAND_QUEUE_LENGTH]; |   std::string command_queue_[COMMAND_QUEUE_LENGTH]; | ||||||
|   uint8_t command_queue_position_ = 0; |   uint8_t command_queue_position_ = 0; | ||||||
| @@ -216,7 +217,7 @@ class Pipsolar : public uart::UARTDevice, public PollingComponent { | |||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   uint8_t last_polling_command_ = 0; |   uint8_t last_polling_command_ = 0; | ||||||
|   PollingCommand used_polling_commands_[15]; |   PollingCommand used_polling_commands_[POLLING_COMMANDS_MAX]; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace pipsolar | }  // namespace pipsolar | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user