diff --git a/platformio.ini b/platformio.ini index b258e926..9680cb1f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -38,7 +38,7 @@ platform = espressif8266@1.8.0 board = nodemcuv2 framework = arduino lib_deps = ${common.lib_deps} -build_flags = ${common.build_flags} +build_flags = ${common.build_flags} -DESPHOME_LOG_LEVEL=6 src_filter = ${common.src_filter} + [env:custombmp180] diff --git a/src/esphome/api/api_server.cpp b/src/esphome/api/api_server.cpp index b1e61ab6..b7532bbf 100644 --- a/src/esphome/api/api_server.cpp +++ b/src/esphome/api/api_server.cpp @@ -90,8 +90,10 @@ void APIServer::loop() { ESP_LOGE(TAG, "No client connected to API. Rebooting..."); reboot("api"); } + this->status_set_warning(); } else { this->last_connected_ = now; + this->status_clear_warning(); } } } @@ -714,6 +716,12 @@ bool APIConnection::send_buffer(APIMessageType type) { } void APIConnection::loop() { + if (!network_is_connected()) { + // when network is disconnected force disconnect immediately + // don't wait for timeout + this->fatal_error_(); + return; + } if (this->client_->disconnected()) { // failsafe for disconnect logic this->on_disconnect_(); diff --git a/src/esphome/application.h b/src/esphome/application.h index e61c9519..375f2372 100644 --- a/src/esphome/application.h +++ b/src/esphome/application.h @@ -334,6 +334,9 @@ class Application { template GlobalVariableComponent *make_global_variable(); template GlobalVariableComponent *make_global_variable(T initial_value); + template + GlobalVariableComponent *make_global_variable( + std::array::type, std::extent::value> initial_value); /* _ _ _______ ____ __ __ _______ _____ ____ _ _ * /\ | | | |__ __/ __ \| \/ | /\|__ __|_ _/ __ \| \ | | @@ -1233,6 +1236,11 @@ template GlobalVariableComponent *Application::make_global_variab template GlobalVariableComponent *Application::make_global_variable(T initial_value) { return this->register_component(new GlobalVariableComponent(initial_value)); } +template +GlobalVariableComponent *Application::make_global_variable( + std::array::type, std::extent::value> initial_value) { + return this->register_component(new GlobalVariableComponent(initial_value)); +} #ifdef USE_NEO_PIXEL_BUS_LIGHT template diff --git a/src/esphome/automation.h b/src/esphome/automation.h index bc3c4b28..c55822d5 100644 --- a/src/esphome/automation.h +++ b/src/esphome/automation.h @@ -276,6 +276,8 @@ template class GlobalVariableComponent : public Component { public: explicit GlobalVariableComponent(); explicit GlobalVariableComponent(T initial_value); + explicit GlobalVariableComponent( + std::array::type, std::extent::value> initial_value); T &value(); diff --git a/src/esphome/automation.tcc b/src/esphome/automation.tcc index c0e0f40c..cecee19d 100644 --- a/src/esphome/automation.tcc +++ b/src/esphome/automation.tcc @@ -207,6 +207,11 @@ template ScriptStopAction *Script::make_stop_action() { template GlobalVariableComponent::GlobalVariableComponent() {} template GlobalVariableComponent::GlobalVariableComponent(T initial_value) : value_(initial_value) {} +template +GlobalVariableComponent::GlobalVariableComponent( + std::array::type, std::extent::value> initial_value) { + memcpy(this->value_, initial_value.data(), sizeof(T)); +} template T &GlobalVariableComponent::value() { return this->value_; } template void GlobalVariableComponent::setup() { if (this->restore_value_) { diff --git a/src/esphome/binary_sensor/binary_sensor.cpp b/src/esphome/binary_sensor/binary_sensor.cpp index fec734c7..302e1564 100644 --- a/src/esphome/binary_sensor/binary_sensor.cpp +++ b/src/esphome/binary_sensor/binary_sensor.cpp @@ -194,15 +194,15 @@ void MultiClickTrigger::on_state_(bool state) { MultiClickTriggerEvent evt = this->timing_[*this->at_index_]; if (evt.max_length != 4294967294UL) { - ESP_LOGV(TAG, "A i=%u min=%u max=%u", *this->at_index_, evt.min_length, evt.max_length); + ESP_LOGV(TAG, "A i=%u min=%u max=%u", *this->at_index_, evt.min_length, evt.max_length); // NOLINT this->schedule_is_valid_(evt.min_length); this->schedule_is_not_valid_(evt.max_length); } else if (*this->at_index_ + 1 != this->timing_.size()) { - ESP_LOGV(TAG, "B i=%u min=%u", *this->at_index_, evt.min_length); + ESP_LOGV(TAG, "B i=%u min=%u", *this->at_index_, evt.min_length); // NOLINT this->cancel_timeout("is_not_valid"); this->schedule_is_valid_(evt.min_length); } else { - ESP_LOGV(TAG, "C i=%u min=%u", *this->at_index_, evt.min_length); + ESP_LOGV(TAG, "C i=%u min=%u", *this->at_index_, evt.min_length); // NOLINT this->is_valid_ = false; this->cancel_timeout("is_not_valid"); this->set_timeout("trigger", evt.min_length, [this]() { this->trigger_(); }); diff --git a/src/esphome/esppreferences.cpp b/src/esphome/esppreferences.cpp index 9b75d434..fe12e8be 100644 --- a/src/esphome/esppreferences.cpp +++ b/src/esphome/esppreferences.cpp @@ -32,7 +32,7 @@ bool ESPPreferenceObject::load_() { bool valid = this->data_[this->length_words_] == this->calculate_crc_(); - ESP_LOGVV(TAG, "LOAD %u: valid=%s, 0=0x%08X 1=0x%08X (Type=%u, CRC=0x%08X)", this->rtc_offset_, YESNO(valid), + ESP_LOGVV(TAG, "LOAD %zu: valid=%s, 0=0x%08X 1=0x%08X (Type=%u, CRC=0x%08X)", this->rtc_offset_, YESNO(valid), this->data_[0], this->data_[1], this->type_, this->calculate_crc_()); return valid; } @@ -45,7 +45,7 @@ bool ESPPreferenceObject::save_() { this->data_[this->length_words_] = this->calculate_crc_(); if (!this->save_internal_()) return false; - ESP_LOGVV(TAG, "SAVE %u: 0=0x%08X 1=0x%08X (Type=%u, CRC=0x%08X)", this->rtc_offset_, this->data_[0], this->data_[1], + ESP_LOGVV(TAG, "SAVE %zu: 0=0x%08X 1=0x%08X (Type=%u, CRC=0x%08X)", this->rtc_offset_, this->data_[0], this->data_[1], this->type_, this->calculate_crc_()); return true; } @@ -80,7 +80,6 @@ static inline bool esp_rtc_user_mem_write(uint32_t index, uint32_t value) { auto *ptr = &ESP_RTC_USER_MEM[index]; #ifdef USE_ESP8266_PREFERENCES_FLASH if (*ptr != value) { - ESP_LOGV(TAG, "RTC flash is dirty..."); esp8266_preferences_modified = true; } #endif diff --git a/src/esphome/light/light_color_values.cpp b/src/esphome/light/light_color_values.cpp index 60ecae70..f2eda230 100644 --- a/src/esphome/light/light_color_values.cpp +++ b/src/esphome/light/light_color_values.cpp @@ -82,18 +82,33 @@ void LightColorValues::normalize_color(const LightTraits &traits) { float max_value = fmaxf(this->get_red(), fmaxf(this->get_green(), this->get_blue())); if (traits.has_rgb_white_value()) { max_value = fmaxf(max_value, this->get_white()); - this->set_white(this->get_white() / max_value); + if (max_value == 0.0f) { + this->set_white(1.0f); + } else { + this->set_white(this->get_white() / max_value); + } + } + if (max_value == 0.0f) { + this->set_red(1.0f); + this->set_green(1.0f); + this->set_blue(1.0f); + } else { + this->set_red(this->get_red() / max_value); + this->set_green(this->get_green() / max_value); + this->set_blue(this->get_blue() / max_value); } - this->set_red(this->get_red() / max_value); - this->set_green(this->get_green() / max_value); - this->set_blue(this->get_blue() / max_value); } if (traits.has_brightness() && this->get_brightness() == 0.0f) { - // 0% brightness means off - this->set_state(false); - // reset brightness to 100% (0% brightness is not allowed) - this->set_brightness(1.0f); + if (traits.has_rgb_white_value()) { + // 0% brightness for RGBW[W] means no RGB channel, but white channel on. + // do nothing + } else { + // 0% brightness means off + this->set_state(false); + // reset brightness to 100% + this->set_brightness(1.0f); + } } } diff --git a/src/esphome/light/light_state.cpp b/src/esphome/light/light_state.cpp index 6bacd19d..6618b454 100644 --- a/src/esphome/light/light_state.cpp +++ b/src/esphome/light/light_state.cpp @@ -124,14 +124,14 @@ void LightState::dump_json(JsonObject &root) { } struct LightStateRTCState { - bool state; - float brightness; - float red; - float green; - float blue; - float white; - float color_temp; - uint32_t effect; + bool state{false}; + float brightness{1.0f}; + float red{1.0f}; + float green{1.0f}; + float blue{1.0f}; + float white{1.0f}; + float color_temp{1.0f}; + uint32_t effect{0}; }; void LightState::setup() { @@ -143,9 +143,9 @@ void LightState::setup() { } this->rtc_ = global_preferences.make_preference(this->get_object_id_hash()); - LightStateRTCState recovered; - if (!this->rtc_.load(&recovered)) - return; + LightStateRTCState recovered{}; + // Attempt to load from preferences, else fall back to default values from struct + this->rtc_.load(&recovered); auto call = this->make_call(); call.set_state(recovered.state); @@ -438,7 +438,7 @@ void LightState::StateCall::perform() const { ESP_LOGD(TAG, " Color Temperature: %.1f mireds", v.get_color_temperature()); } - v.normalize_color(this->state_->output_->get_traits()); + v.normalize_color(traits); if (traits.has_rgb() && (this->red_.has_value() || this->green_.has_value() || this->blue_.has_value())) { if (traits.has_rgb_white_value() && this->white_.has_value()) { diff --git a/src/esphome/remote/remote_receiver.cpp b/src/esphome/remote/remote_receiver.cpp index b76a8a80..2aa7a624 100644 --- a/src/esphome/remote/remote_receiver.cpp +++ b/src/esphome/remote/remote_receiver.cpp @@ -326,8 +326,8 @@ void RemoteReceiverComponent::loop() { // TODO: Handle case when loop() is not called quickly enough to catch idle return; - ESP_LOGVV(TAG, "read_at=%u write_at=%u dist=%u now=%u end=%u", this->buffer_read_at_, write_at, dist, now, - this->buffer_[write_at]); + ESP_LOGVV(TAG, "read_at=%u write_at=%u dist=%u now=%u end=%u", s.buffer_read_at, write_at, dist, now, + s.buffer[write_at]); // Skip first value, it's from the previous idle level s.buffer_read_at = (s.buffer_read_at + 1) % s.buffer_size; @@ -345,8 +345,8 @@ void RemoteReceiverComponent::loop() { break; } - ESP_LOGVV(TAG, " i=%u buffer[%u]=%u - buffer[%u]=%u -> %d", i, this->buffer_read_at_, - this->buffer_[this->buffer_read_at_], prev, this->buffer_[prev], multiplier * delta); + ESP_LOGVV(TAG, " i=%u buffer[%u]=%u - buffer[%u]=%u -> %d", i, s.buffer_read_at, s.buffer[s.buffer_read_at], prev, + s.buffer[prev], multiplier * delta); this->temp_.push_back(multiplier * delta); prev = s.buffer_read_at; s.buffer_read_at = (s.buffer_read_at + 1) % s.buffer_size; diff --git a/src/esphome/sensor/cse7766.cpp b/src/esphome/sensor/cse7766.cpp index 929b6714..600c9048 100644 --- a/src/esphome/sensor/cse7766.cpp +++ b/src/esphome/sensor/cse7766.cpp @@ -43,34 +43,8 @@ bool CSE7766Component::check_byte_() { uint8_t index = this->raw_data_index_; uint8_t byte = this->raw_data_[index]; if (index == 0) { - // Header - 0x55 - // These messages are verbose because the CSE7766 - // reports these when no load is attached. If the checksum doesn't match - // though, then we should print a warning. - if (byte == 0xAA) { - ESP_LOGV(TAG, "CSE7766 not calibrated!"); - return false; - } - if ((byte & 0xF0) == 0xF0) { - ESP_LOGV(TAG, "CSE7766 reports abnormal hardware: (0x%02X)", byte); - if ((byte >> 3) & 1) { - ESP_LOGV(TAG, " Voltage cycle exceeds range."); - } - if ((byte >> 2) & 1) { - ESP_LOGV(TAG, " Current cycle exceeds range."); - } - if ((byte >> 1) & 1) { - ESP_LOGV(TAG, " Power cycle exceeds range."); - } - if ((byte >> 0) & 1) { - ESP_LOGV(TAG, " Coefficient storage area is abnormal."); - } - return false; - } - if (byte != 0x55) { - ESP_LOGV(TAG, "Invalid Header Start from CSE7766: 0x%02X!", byte); - return false; - } + // Header, usually 0x55, contains data about calibration etc. + // this is validated in parse_data_ return true; } @@ -103,6 +77,18 @@ void CSE7766Component::parse_data_() { ESP_LOGVV(TAG, " i=%u: 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", i, BYTE_TO_BINARY(this->raw_data_[i]), this->raw_data_[i]); } + + uint8_t header1 = this->raw_data_[0]; + if (header1 == 0xAA) { + ESP_LOGW(TAG, "CSE7766 not calibrated!"); + return; + } + if ((header1 & 0xF0) == 0xF0 && ((header1 >> 0) & 1) == 1) { + ESP_LOGW(TAG, "CSE7766 reports abnormal hardware: (0x%02X)", header1); + ESP_LOGW(TAG, " Coefficient storage area is abnormal."); + return; + } + const uint32_t now = micros(); const float d = (now - this->last_reading_) / 1000.0f; this->last_reading_ = now; @@ -116,19 +102,25 @@ void CSE7766Component::parse_data_() { uint8_t adj = this->raw_data_[20]; - if ((adj >> 6 != 0) && voltage_cycle != 0) { + if ((adj >> 6 != 0) && voltage_cycle != 0 && + // voltage cycle exceeds range + ((header1 >> 3) & 1) == 0) { // voltage cycle of serial port outputted is a complete cycle; float voltage = voltage_calib / float(voltage_cycle); this->voltage_acc_ += voltage * d; } - if ((adj >> 5 != 0) && power_cycle != 0 && current_cycle != 0) { + if ((adj >> 5 != 0) && power_cycle != 0 && current_cycle != 0 && + // current cycle exceeds range + ((header1 >> 2) & 1) == 0) { // indicates current cycle of serial port outputted is a complete cycle; float current = current_calib / float(current_cycle); this->current_acc_ += current * d; } - if ((adj >> 4 != 0) && power_cycle != 0) { + if ((adj >> 4 != 0) && power_cycle != 0 && + // power cycle exceeds range + ((header1 >> 1) & 1) == 0) { // power cycle of serial port outputted is a complete cycle; float active_power = power_calib / float(power_cycle); this->power_acc_ += active_power * d; diff --git a/src/esphome/sensor/rotary_encoder.cpp b/src/esphome/sensor/rotary_encoder.cpp index e0fc8f01..35275f7c 100644 --- a/src/esphome/sensor/rotary_encoder.cpp +++ b/src/esphome/sensor/rotary_encoder.cpp @@ -88,7 +88,7 @@ void ICACHE_RAM_ATTR HOT RotaryEncoderSensorStore::gpio_intr(RotaryEncoderSensor if (arg->pin_b->digital_read()) input_state |= STATE_PIN_B_HIGH; - uint8_t new_state = STATE_LOOKUP_TABLE[input_state]; + uint16_t new_state = STATE_LOOKUP_TABLE[input_state]; if ((new_state & arg->resolution & STATE_HAS_INCREMENTED) != 0) { if (arg->counter < arg->max_value) arg->counter++; diff --git a/src/esphome/wifi_component.cpp b/src/esphome/wifi_component.cpp index 0295f73c..c3f46b56 100644 --- a/src/esphome/wifi_component.cpp +++ b/src/esphome/wifi_component.cpp @@ -174,7 +174,7 @@ void WiFiComponent::start_connecting(const WiFiAP &ap, bool two) { } else { ESP_LOGV(TAG, " BSSID: Not Set"); } - ESP_LOGV(TAG, " Password: " LOG_SECRET("'%s'"), ap.get_password()); + ESP_LOGV(TAG, " Password: " LOG_SECRET("'%s'"), ap.get_password().c_str()); if (ap.get_channel().has_value()) { ESP_LOGV(TAG, " Channel: %u", *ap.get_channel()); } else { @@ -779,6 +779,12 @@ const char *get_disconnect_reason_str(uint8_t reason) { } void WiFiComponent::wifi_event_callback(System_Event_t *event) { +#ifdef ESPHOME_LOG_HAS_VERBOSE + // TODO: this callback is called while in cont context, so delay will fail + // We need to defer the log messages until we're out of this context + // only affects verbose log level + // reproducible by enabling verbose log level and letting the ESP disconnect and + // then reconnect to WiFi. switch (event->event) { case EVENT_STAMODE_CONNECTED: { auto it = event->event_info.connected; @@ -794,7 +800,7 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) { char buf[33]; memcpy(buf, it.ssid, it.ssid_len); buf[it.ssid_len] = '\0'; - ESP_LOGW(TAG, "Event: Disconnected ssid='%s' bssid=%s reason='%s'", buf, format_mac_addr(it.bssid).c_str(), + ESP_LOGV(TAG, "Event: Disconnected ssid='%s' bssid=%s reason='%s'", buf, format_mac_addr(it.bssid).c_str(), get_disconnect_reason_str(it.reason)); break; } @@ -844,6 +850,7 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) { default: break; } +#endif if (event->event == EVENT_STAMODE_DISCONNECTED) { global_wifi_component->error_from_callback_ = true;