mirror of
https://github.com/esphome/esphome.git
synced 2025-09-03 11:52:20 +01:00
Merge branch 'slot_freed_too_early' into integration
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_) {
|
return;
|
||||||
this->send_next_poll_();
|
|
||||||
this->last_poll_ = millis();
|
|
||||||
}
|
|
||||||
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,37 +808,43 @@ 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;
|
|
||||||
if (this->used_polling_commands_[this->last_polling_command_].length == 0) {
|
for (uint8_t i = 0; i < POLLING_COMMANDS_MAX; i++) {
|
||||||
this->last_polling_command_ = 0;
|
this->last_polling_command_ = (this->last_polling_command_ + 1) % POLLING_COMMANDS_MAX;
|
||||||
|
if (this->used_polling_commands_[this->last_polling_command_].length == 0) {
|
||||||
|
// not enabled
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!this->used_polling_commands_[this->last_polling_command_].needs_update) {
|
||||||
|
// no update requested
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this->state_ = STATE_POLL;
|
||||||
|
this->command_start_millis_ = millis();
|
||||||
|
this->empty_uart_buffer_();
|
||||||
|
this->read_pos_ = 0;
|
||||||
|
crc16 = this->pipsolar_crc_(this->used_polling_commands_[this->last_polling_command_].command,
|
||||||
|
this->used_polling_commands_[this->last_polling_command_].length);
|
||||||
|
this->write_array(this->used_polling_commands_[this->last_polling_command_].command,
|
||||||
|
this->used_polling_commands_[this->last_polling_command_].length);
|
||||||
|
// checksum
|
||||||
|
this->write(((uint8_t) ((crc16) >> 8))); // highbyte
|
||||||
|
this->write(((uint8_t) ((crc16) &0xff))); // lowbyte
|
||||||
|
// end Byte
|
||||||
|
this->write(0x0D);
|
||||||
|
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_].length);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if (this->used_polling_commands_[this->last_polling_command_].length == 0) {
|
return false;
|
||||||
// no command specified
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->state_ = STATE_POLL;
|
|
||||||
this->command_start_millis_ = millis();
|
|
||||||
this->empty_uart_buffer_();
|
|
||||||
this->read_pos_ = 0;
|
|
||||||
crc16 = this->pipsolar_crc_(this->used_polling_commands_[this->last_polling_command_].command,
|
|
||||||
this->used_polling_commands_[this->last_polling_command_].length);
|
|
||||||
this->write_array(this->used_polling_commands_[this->last_polling_command_].command,
|
|
||||||
this->used_polling_commands_[this->last_polling_command_].length);
|
|
||||||
// checksum
|
|
||||||
this->write(((uint8_t) ((crc16) >> 8))); // highbyte
|
|
||||||
this->write(((uint8_t) ((crc16) &0xff))); // lowbyte
|
|
||||||
// end Byte
|
|
||||||
this->write(0x0D);
|
|
||||||
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_].length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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