diff --git a/esphome/__main__.py b/esphome/__main__.py index 5c197ff486..cf2741dbdb 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -38,7 +38,7 @@ from esphome.const import ( SECRETS_FILES, ) from esphome.core import CORE, EsphomeError, coroutine -from esphome.helpers import indent, is_ip_address +from esphome.helpers import indent, is_ip_address, get_bool_env from esphome.log import Fore, color, setup_log from esphome.util import ( get_serial_ports, @@ -731,7 +731,11 @@ POST_CONFIG_ACTIONS = { def parse_args(argv): options_parser = argparse.ArgumentParser(add_help=False) options_parser.add_argument( - "-v", "--verbose", help="Enable verbose ESPHome logs.", action="store_true" + "-v", + "--verbose", + help="Enable verbose ESPHome logs.", + action="store_true", + default=get_bool_env("ESPHOME_VERBOSE"), ) options_parser.add_argument( "-q", "--quiet", help="Disable all ESPHome logs.", action="store_true" diff --git a/esphome/components/ledc/ledc_output.cpp b/esphome/components/ledc/ledc_output.cpp index 90e11fe4ad..7f91eb2d7a 100644 --- a/esphome/components/ledc/ledc_output.cpp +++ b/esphome/components/ledc/ledc_output.cpp @@ -8,6 +8,8 @@ #endif #include +#include + #define CLOCK_FREQUENCY 80e6f #ifdef USE_ARDUINO @@ -120,13 +122,17 @@ void LEDCOutput::write_state(float state) { ledcWrite(this->channel_, duty); #endif #ifdef USE_ESP_IDF +#if !defined(USE_ESP32_VARIANT_ESP32C3) || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0)) // ensure that 100% on is not 99.975% on + // note: on the C3, this tweak will result in the outputs turning off at 100%, so it has been omitted if ((duty == max_duty) && (max_duty != 1)) { duty = max_duty + 1; } +#endif auto speed_mode = get_speed_mode(channel_); auto chan_num = static_cast(channel_ % 8); int hpoint = ledc_angle_to_htop(this->phase_angle_, this->bit_depth_); + ESP_LOGV(TAG, "Setting duty: %" PRIu32 " on channel %u", duty, this->channel_); ledc_set_duty_with_hpoint(speed_mode, chan_num, duty, hpoint); ledc_update_duty(speed_mode, chan_num); #endif diff --git a/esphome/components/lvgl/automation.py b/esphome/components/lvgl/automation.py index a39f589136..efcac977ab 100644 --- a/esphome/components/lvgl/automation.py +++ b/esphome/components/lvgl/automation.py @@ -5,6 +5,7 @@ from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_TIMEOUT +from esphome.cpp_generator import RawExpression from esphome.cpp_types import nullptr from .defines import ( @@ -26,6 +27,7 @@ from .lvcode import ( add_line_marks, lv, lv_add, + lv_expr, lv_obj, lvgl_comp, ) @@ -38,7 +40,13 @@ from .types import ( lv_disp_t, lv_obj_t, ) -from .widgets import Widget, get_widgets, lv_scr_act, set_obj_properties +from .widgets import ( + Widget, + get_widgets, + lv_scr_act, + set_obj_properties, + wait_for_widgets, +) async def action_to_code( @@ -48,10 +56,12 @@ async def action_to_code( template_arg, args, ): + await wait_for_widgets() async with LambdaContext(parameters=args, where=action_id) as context: + with LvConditional(lv_expr.is_pre_initialise()): + context.add(RawExpression("return")) for widget in widgets: - with LvConditional(widget.obj != nullptr): - await action(widget) + await action(widget) var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda()) return var diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index 92f7a880c3..6882986e7c 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -294,6 +294,13 @@ void LvglComponent::loop() { } lv_timer_handler_run_in_period(5); } +bool lv_is_pre_initialise() { + if (!lv_is_initialized()) { + ESP_LOGE(TAG, "LVGL call before component is initialised"); + return true; + } + return false; +} #ifdef USE_LVGL_IMAGE lv_img_dsc_t *lv_img_from(image::Image *src, lv_img_dsc_t *img_dsc) { diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index 3a3d1aa6c5..df3d4aa68c 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -40,6 +40,7 @@ namespace lvgl { extern lv_event_code_t lv_api_event; // NOLINT extern lv_event_code_t lv_update_event; // NOLINT +extern bool lv_is_pre_initialise(); #ifdef USE_LVGL_COLOR inline lv_color_t lv_color_from(Color color) { return lv_color_make(color.red, color.green, color.blue); } #endif // USE_LVGL_COLOR diff --git a/esphome/components/lvgl/widgets/__init__.py b/esphome/components/lvgl/widgets/__init__.py index 4abb25c61d..50da6e131d 100644 --- a/esphome/components/lvgl/widgets/__init__.py +++ b/esphome/components/lvgl/widgets/__init__.py @@ -1,3 +1,4 @@ +import asyncio import sys from typing import Any, Union @@ -223,6 +224,11 @@ async def get_widget_(wid: Widget): return await FakeAwaitable(get_widget_generator(wid)) +async def wait_for_widgets(): + while not Widget.widgets_completed: + await asyncio.sleep(0) + + async def get_widgets(config: Union[dict, list], id: str = CONF_ID) -> list[Widget]: if not config: return [] diff --git a/esphome/components/lvgl/widgets/line.py b/esphome/components/lvgl/widgets/line.py index 8ce4b1965f..4c6439fde4 100644 --- a/esphome/components/lvgl/widgets/line.py +++ b/esphome/components/lvgl/widgets/line.py @@ -3,7 +3,7 @@ import functools import esphome.codegen as cg import esphome.config_validation as cv -from ..defines import CONF_MAIN, literal +from ..defines import CONF_MAIN from ..lvcode import lv from ..types import LvType from . import Widget, WidgetType @@ -38,13 +38,15 @@ LINE_SCHEMA = { class LineType(WidgetType): def __init__(self): - super().__init__(CONF_LINE, LvType("lv_line_t"), (CONF_MAIN,), LINE_SCHEMA) + super().__init__( + CONF_LINE, LvType("lv_line_t"), (CONF_MAIN,), LINE_SCHEMA, modify_schema={} + ) async def to_code(self, w: Widget, config): """For a line object, create and add the points""" - data = literal(config[CONF_POINTS]) - points = cg.static_const_array(config[CONF_POINT_LIST_ID], data) - lv.line_set_points(w.obj, points, len(data)) + if data := config.get(CONF_POINTS): + points = cg.static_const_array(config[CONF_POINT_LIST_ID], data) + lv.line_set_points(w.obj, points, len(data)) line_spec = LineType() diff --git a/esphome/components/lvgl/widgets/msgbox.py b/esphome/components/lvgl/widgets/msgbox.py index 63c4326c7c..c377af6bde 100644 --- a/esphome/components/lvgl/widgets/msgbox.py +++ b/esphome/components/lvgl/widgets/msgbox.py @@ -13,7 +13,7 @@ from ..defines import ( TYPE_FLEX, literal, ) -from ..helpers import add_lv_use +from ..helpers import add_lv_use, lvgl_components_required from ..lv_validation import lv_bool, lv_pct, lv_text from ..lvcode import ( EVENT_ARG, @@ -72,6 +72,7 @@ async def msgbox_to_code(conf): *buttonmatrix_spec.get_uses(), *button_spec.get_uses(), ) + lvgl_components_required.add("BUTTONMATRIX") messagebox_id = conf[CONF_ID] outer = lv_Pvariable(lv_obj_t, messagebox_id.id) buttonmatrix = new_Pvariable( diff --git a/esphome/components/pipsolar/pipsolar.cpp b/esphome/components/pipsolar/pipsolar.cpp index 2cd1aeba44..c4bc018b75 100644 --- a/esphome/components/pipsolar/pipsolar.cpp +++ b/esphome/components/pipsolar/pipsolar.cpp @@ -136,6 +136,9 @@ void Pipsolar::loop() { if (this->output_source_priority_battery_switch_) { this->output_source_priority_battery_switch_->publish_state(value_output_source_priority_ == 2); } + if (this->output_source_priority_hybrid_switch_) { + this->output_source_priority_hybrid_switch_->publish_state(value_output_source_priority_ == 3); + } if (this->charger_source_priority_) { this->charger_source_priority_->publish_state(value_charger_source_priority_); } diff --git a/esphome/components/pipsolar/pipsolar.h b/esphome/components/pipsolar/pipsolar.h index f20f44f095..373911b2d7 100644 --- a/esphome/components/pipsolar/pipsolar.h +++ b/esphome/components/pipsolar/pipsolar.h @@ -174,6 +174,7 @@ class Pipsolar : public uart::UARTDevice, public PollingComponent { PIPSOLAR_SWITCH(output_source_priority_utility_switch, QPIRI) PIPSOLAR_SWITCH(output_source_priority_solar_switch, QPIRI) PIPSOLAR_SWITCH(output_source_priority_battery_switch, QPIRI) + PIPSOLAR_SWITCH(output_source_priority_hybrid_switch, QPIRI) PIPSOLAR_SWITCH(input_voltage_range_switch, QPIRI) PIPSOLAR_SWITCH(pv_ok_condition_for_parallel_switch, QPIRI) PIPSOLAR_SWITCH(pv_power_balance_switch, QPIRI) diff --git a/esphome/components/pipsolar/switch/__init__.py b/esphome/components/pipsolar/switch/__init__.py index 7658c7d4f8..80bcdad62e 100644 --- a/esphome/components/pipsolar/switch/__init__.py +++ b/esphome/components/pipsolar/switch/__init__.py @@ -9,6 +9,7 @@ DEPENDENCIES = ["uart"] CONF_OUTPUT_SOURCE_PRIORITY_UTILITY = "output_source_priority_utility" CONF_OUTPUT_SOURCE_PRIORITY_SOLAR = "output_source_priority_solar" CONF_OUTPUT_SOURCE_PRIORITY_BATTERY = "output_source_priority_battery" +CONF_OUTPUT_SOURCE_PRIORITY_HYBRID = "output_source_priority_hybrid" CONF_INPUT_VOLTAGE_RANGE = "input_voltage_range" CONF_PV_OK_CONDITION_FOR_PARALLEL = "pv_ok_condition_for_parallel" CONF_PV_POWER_BALANCE = "pv_power_balance" @@ -17,6 +18,7 @@ TYPES = { CONF_OUTPUT_SOURCE_PRIORITY_UTILITY: ("POP00", None), CONF_OUTPUT_SOURCE_PRIORITY_SOLAR: ("POP01", None), CONF_OUTPUT_SOURCE_PRIORITY_BATTERY: ("POP02", None), + CONF_OUTPUT_SOURCE_PRIORITY_HYBRID: ("POP03", None), CONF_INPUT_VOLTAGE_RANGE: ("PGR01", "PGR00"), CONF_PV_OK_CONDITION_FOR_PARALLEL: ("PPVOKC1", "PPVOKC0"), CONF_PV_POWER_BALANCE: ("PSPB1", "PSPB0"), diff --git a/esphome/components/tuya/__init__.py b/esphome/components/tuya/__init__.py index 2eaaa2a625..0738f9b6a4 100644 --- a/esphome/components/tuya/__init__.py +++ b/esphome/components/tuya/__init__.py @@ -15,6 +15,7 @@ CONF_DATAPOINT_TYPE = "datapoint_type" CONF_STATUS_PIN = "status_pin" tuya_ns = cg.esphome_ns.namespace("tuya") +TuyaDatapointType = tuya_ns.enum("TuyaDatapointType", is_class=True) Tuya = tuya_ns.class_("Tuya", cg.Component, uart.UARTDevice) DPTYPE_ANY = "any" diff --git a/esphome/components/tuya/number/__init__.py b/esphome/components/tuya/number/__init__.py index 4dae6d8d60..25be6329ab 100644 --- a/esphome/components/tuya/number/__init__.py +++ b/esphome/components/tuya/number/__init__.py @@ -8,18 +8,36 @@ from esphome.const import ( CONF_MIN_VALUE, CONF_MULTIPLY, CONF_STEP, + CONF_INITIAL_VALUE, ) -from .. import tuya_ns, CONF_TUYA_ID, Tuya +from .. import tuya_ns, CONF_TUYA_ID, Tuya, TuyaDatapointType DEPENDENCIES = ["tuya"] CODEOWNERS = ["@frankiboy1"] +CONF_DATAPOINT_HIDDEN = "datapoint_hidden" +CONF_DATAPOINT_TYPE = "datapoint_type" + TuyaNumber = tuya_ns.class_("TuyaNumber", number.Number, cg.Component) +DATAPOINT_TYPES = { + "int": TuyaDatapointType.INTEGER, + "uint": TuyaDatapointType.INTEGER, + "enum": TuyaDatapointType.ENUM, +} + def validate_min_max(config): - if config[CONF_MAX_VALUE] <= config[CONF_MIN_VALUE]: + max_value = config[CONF_MAX_VALUE] + min_value = config[CONF_MIN_VALUE] + if max_value <= min_value: raise cv.Invalid("max_value must be greater than min_value") + if hidden_config := config.get(CONF_DATAPOINT_HIDDEN): + if (initial_value := hidden_config.get(CONF_INITIAL_VALUE, None)) is not None: + if (initial_value > max_value) or (initial_value < min_value): + raise cv.Invalid( + f"{CONF_INITIAL_VALUE} must be a value between {CONF_MAX_VALUE} and {CONF_MIN_VALUE}" + ) return config @@ -33,6 +51,16 @@ CONFIG_SCHEMA = cv.All( cv.Required(CONF_MIN_VALUE): cv.float_, cv.Required(CONF_STEP): cv.positive_float, cv.Optional(CONF_MULTIPLY, default=1.0): cv.float_, + cv.Optional(CONF_DATAPOINT_HIDDEN): cv.All( + cv.Schema( + { + cv.Required(CONF_DATAPOINT_TYPE): cv.enum( + DATAPOINT_TYPES, lower=True + ), + cv.Optional(CONF_INITIAL_VALUE): cv.float_, + } + ) + ), } ) .extend(cv.COMPONENT_SCHEMA), @@ -56,3 +84,9 @@ async def to_code(config): cg.add(var.set_tuya_parent(parent)) cg.add(var.set_number_id(config[CONF_NUMBER_DATAPOINT])) + if hidden_config := config.get(CONF_DATAPOINT_HIDDEN): + cg.add(var.set_datapoint_type(hidden_config[CONF_DATAPOINT_TYPE])) + if ( + hidden_init_value := hidden_config.get(CONF_INITIAL_VALUE, None) + ) is not None: + cg.add(var.set_datapoint_initial_value(hidden_init_value)) diff --git a/esphome/components/tuya/number/tuya_number.cpp b/esphome/components/tuya/number/tuya_number.cpp index e883c72d3d..7eeb08fde2 100644 --- a/esphome/components/tuya/number/tuya_number.cpp +++ b/esphome/components/tuya/number/tuya_number.cpp @@ -15,8 +15,18 @@ void TuyaNumber::setup() { ESP_LOGV(TAG, "MCU reported number %u is: %u", datapoint.id, datapoint.value_enum); this->publish_state(datapoint.value_enum); } + if ((this->type_) && (this->type_ != datapoint.type)) { + ESP_LOGW(TAG, "Reported type (%d) different than previously set (%d)!", static_cast(datapoint.type), + static_cast(*this->type_)); + } this->type_ = datapoint.type; }); + + this->parent_->add_on_initialized_callback([this] { + if ((this->initial_value_) && (this->type_)) { + this->control(*this->initial_value_); + } + }); } void TuyaNumber::control(float value) { @@ -33,6 +43,15 @@ void TuyaNumber::control(float value) { void TuyaNumber::dump_config() { LOG_NUMBER("", "Tuya Number", this); ESP_LOGCONFIG(TAG, " Number has datapoint ID %u", this->number_id_); + if (this->type_) { + ESP_LOGCONFIG(TAG, " Datapoint type is %d", static_cast(*this->type_)); + } else { + ESP_LOGCONFIG(TAG, " Datapoint type is unknown"); + } + + if (this->initial_value_) { + ESP_LOGCONFIG(TAG, " Initial Value: %f", *this->initial_value_); + } } } // namespace tuya diff --git a/esphome/components/tuya/number/tuya_number.h b/esphome/components/tuya/number/tuya_number.h index f64dac8957..545584128e 100644 --- a/esphome/components/tuya/number/tuya_number.h +++ b/esphome/components/tuya/number/tuya_number.h @@ -3,6 +3,7 @@ #include "esphome/core/component.h" #include "esphome/components/tuya/tuya.h" #include "esphome/components/number/number.h" +#include "esphome/core/optional.h" namespace esphome { namespace tuya { @@ -13,6 +14,8 @@ class TuyaNumber : public number::Number, public Component { void dump_config() override; void set_number_id(uint8_t number_id) { this->number_id_ = number_id; } void set_write_multiply(float factor) { multiply_by_ = factor; } + void set_datapoint_type(TuyaDatapointType type) { type_ = type; } + void set_datapoint_initial_value(float value) { this->initial_value_ = value; } void set_tuya_parent(Tuya *parent) { this->parent_ = parent; } @@ -22,7 +25,8 @@ class TuyaNumber : public number::Number, public Component { Tuya *parent_; uint8_t number_id_{0}; float multiply_by_{1.0}; - TuyaDatapointType type_{}; + optional type_{}; + optional initial_value_{}; }; } // namespace tuya diff --git a/esphome/core/bytebuffer.cpp b/esphome/core/bytebuffer.cpp index fb2ade3166..65525ecfcf 100644 --- a/esphome/core/bytebuffer.cpp +++ b/esphome/core/bytebuffer.cpp @@ -1,19 +1,64 @@ #include "bytebuffer.h" #include +#include namespace esphome { -ByteBuffer ByteBuffer::create(size_t capacity) { - std::vector data(capacity); - return {data}; -} - -ByteBuffer ByteBuffer::wrap(uint8_t *ptr, size_t len) { +ByteBuffer ByteBuffer::wrap(const uint8_t *ptr, size_t len, Endian endianness) { + // there is a double copy happening here, could be optimized but at cost of clarity. std::vector data(ptr, ptr + len); - return {data}; + ByteBuffer buffer = {data}; + buffer.endianness_ = endianness; + return buffer; } -ByteBuffer ByteBuffer::wrap(std::vector data) { return {std::move(data)}; } +ByteBuffer ByteBuffer::wrap(std::vector const &data, Endian endianness) { + ByteBuffer buffer = {data}; + buffer.endianness_ = endianness; + return buffer; +} + +ByteBuffer ByteBuffer::wrap(uint8_t value) { + ByteBuffer buffer = ByteBuffer(1); + buffer.put_uint8(value); + buffer.flip(); + return buffer; +} + +ByteBuffer ByteBuffer::wrap(uint16_t value, Endian endianness) { + ByteBuffer buffer = ByteBuffer(2, endianness); + buffer.put_uint16(value); + buffer.flip(); + return buffer; +} + +ByteBuffer ByteBuffer::wrap(uint32_t value, Endian endianness) { + ByteBuffer buffer = ByteBuffer(4, endianness); + buffer.put_uint32(value); + buffer.flip(); + return buffer; +} + +ByteBuffer ByteBuffer::wrap(uint64_t value, Endian endianness) { + ByteBuffer buffer = ByteBuffer(8, endianness); + buffer.put_uint64(value); + buffer.flip(); + return buffer; +} + +ByteBuffer ByteBuffer::wrap(float value, Endian endianness) { + ByteBuffer buffer = ByteBuffer(sizeof(float), endianness); + buffer.put_float(value); + buffer.flip(); + return buffer; +} + +ByteBuffer ByteBuffer::wrap(double value, Endian endianness) { + ByteBuffer buffer = ByteBuffer(sizeof(double), endianness); + buffer.put_double(value); + buffer.flip(); + return buffer; +} void ByteBuffer::set_limit(size_t limit) { assert(limit <= this->get_capacity()); @@ -27,108 +72,102 @@ void ByteBuffer::clear() { this->limit_ = this->get_capacity(); this->position_ = 0; } -uint16_t ByteBuffer::get_uint16() { - assert(this->get_remaining() >= 2); - uint16_t value; - if (endianness_ == LITTLE) { - value = this->data_[this->position_++]; - value |= this->data_[this->position_++] << 8; - } else { - value = this->data_[this->position_++] << 8; - value |= this->data_[this->position_++]; - } - return value; +void ByteBuffer::flip() { + this->limit_ = this->position_; + this->position_ = 0; } -uint32_t ByteBuffer::get_uint32() { - assert(this->get_remaining() >= 4); - uint32_t value; - if (endianness_ == LITTLE) { - value = this->data_[this->position_++]; - value |= this->data_[this->position_++] << 8; - value |= this->data_[this->position_++] << 16; - value |= this->data_[this->position_++] << 24; - } else { - value = this->data_[this->position_++] << 24; - value |= this->data_[this->position_++] << 16; - value |= this->data_[this->position_++] << 8; - value |= this->data_[this->position_++]; - } - return value; -} -uint32_t ByteBuffer::get_uint24() { - assert(this->get_remaining() >= 3); - uint32_t value; - if (endianness_ == LITTLE) { - value = this->data_[this->position_++]; - value |= this->data_[this->position_++] << 8; - value |= this->data_[this->position_++] << 16; - } else { - value = this->data_[this->position_++] << 16; - value |= this->data_[this->position_++] << 8; - value |= this->data_[this->position_++]; - } - return value; -} -uint32_t ByteBuffer::get_int24() { - auto value = this->get_uint24(); - uint32_t mask = (~(uint32_t) 0) << 23; - if ((value & mask) != 0) - value |= mask; - return value; -} +/// Getters uint8_t ByteBuffer::get_uint8() { assert(this->get_remaining() >= 1); return this->data_[this->position_++]; } -float ByteBuffer::get_float() { - auto value = this->get_uint32(); - return *(float *) &value; +uint64_t ByteBuffer::get_uint(size_t length) { + assert(this->get_remaining() >= length); + uint64_t value = 0; + if (this->endianness_ == LITTLE) { + this->position_ += length; + auto index = this->position_; + while (length-- != 0) { + value <<= 8; + value |= this->data_[--index]; + } + } else { + while (length-- != 0) { + value <<= 8; + value |= this->data_[this->position_++]; + } + } + return value; } + +uint32_t ByteBuffer::get_int24() { + auto value = this->get_uint24(); + uint32_t mask = (~static_cast(0)) << 23; + if ((value & mask) != 0) + value |= mask; + return value; +} +float ByteBuffer::get_float() { + assert(this->get_remaining() >= sizeof(float)); + auto ui_value = this->get_uint32(); + float value; + memcpy(&value, &ui_value, sizeof(float)); + return value; +} +double ByteBuffer::get_double() { + assert(this->get_remaining() >= sizeof(double)); + auto ui_value = this->get_uint64(); + double value; + memcpy(&value, &ui_value, sizeof(double)); + return value; +} +std::vector ByteBuffer::get_vector(size_t length) { + assert(this->get_remaining() >= length); + auto start = this->data_.begin() + this->position_; + this->position_ += length; + return {start, start + length}; +} + +/// Putters void ByteBuffer::put_uint8(uint8_t value) { assert(this->get_remaining() >= 1); this->data_[this->position_++] = value; } -void ByteBuffer::put_uint16(uint16_t value) { - assert(this->get_remaining() >= 2); +void ByteBuffer::put_uint(uint64_t value, size_t length) { + assert(this->get_remaining() >= length); if (this->endianness_ == LITTLE) { - this->data_[this->position_++] = (uint8_t) value; - this->data_[this->position_++] = (uint8_t) (value >> 8); + while (length-- != 0) { + this->data_[this->position_++] = static_cast(value); + value >>= 8; + } } else { - this->data_[this->position_++] = (uint8_t) (value >> 8); - this->data_[this->position_++] = (uint8_t) value; + this->position_ += length; + auto index = this->position_; + while (length-- != 0) { + this->data_[--index] = static_cast(value); + value >>= 8; + } } } -void ByteBuffer::put_uint24(uint32_t value) { - assert(this->get_remaining() >= 3); - if (this->endianness_ == LITTLE) { - this->data_[this->position_++] = (uint8_t) value; - this->data_[this->position_++] = (uint8_t) (value >> 8); - this->data_[this->position_++] = (uint8_t) (value >> 16); - } else { - this->data_[this->position_++] = (uint8_t) (value >> 16); - this->data_[this->position_++] = (uint8_t) (value >> 8); - this->data_[this->position_++] = (uint8_t) value; - } +void ByteBuffer::put_float(float value) { + static_assert(sizeof(float) == sizeof(uint32_t), "Float sizes other than 32 bit not supported"); + assert(this->get_remaining() >= sizeof(float)); + uint32_t ui_value; + memcpy(&ui_value, &value, sizeof(float)); // this work-around required to silence compiler warnings + this->put_uint32(ui_value); } -void ByteBuffer::put_uint32(uint32_t value) { - assert(this->get_remaining() >= 4); - if (this->endianness_ == LITTLE) { - this->data_[this->position_++] = (uint8_t) value; - this->data_[this->position_++] = (uint8_t) (value >> 8); - this->data_[this->position_++] = (uint8_t) (value >> 16); - this->data_[this->position_++] = (uint8_t) (value >> 24); - } else { - this->data_[this->position_++] = (uint8_t) (value >> 24); - this->data_[this->position_++] = (uint8_t) (value >> 16); - this->data_[this->position_++] = (uint8_t) (value >> 8); - this->data_[this->position_++] = (uint8_t) value; - } +void ByteBuffer::put_double(double value) { + static_assert(sizeof(double) == sizeof(uint64_t), "Double sizes other than 64 bit not supported"); + assert(this->get_remaining() >= sizeof(double)); + uint64_t ui_value; + memcpy(&ui_value, &value, sizeof(double)); + this->put_uint64(ui_value); } -void ByteBuffer::put_float(float value) { this->put_uint32(*(uint32_t *) &value); } -void ByteBuffer::flip() { - this->limit_ = this->position_; - this->position_ = 0; +void ByteBuffer::put_vector(const std::vector &value) { + assert(this->get_remaining() >= value.size()); + std::copy(value.begin(), value.end(), this->data_.begin() + this->position_); + this->position_ += value.size(); } } // namespace esphome diff --git a/esphome/core/bytebuffer.h b/esphome/core/bytebuffer.h index f242e5e333..d44d01f275 100644 --- a/esphome/core/bytebuffer.h +++ b/esphome/core/bytebuffer.h @@ -15,55 +15,103 @@ enum Endian { LITTLE, BIG }; * * There are three variables maintained pointing into the buffer: * - * 0 <= position <= limit <= capacity - * - * capacity: the maximum amount of data that can be stored + * capacity: the maximum amount of data that can be stored - set on construction and cannot be changed * limit: the limit of the data currently available to get or put * position: the current insert or extract position * + * 0 <= position <= limit <= capacity + * * In addition a mark can be set to the current position with mark(). A subsequent call to reset() will restore * the position to the mark. * * The buffer can be marked to be little-endian (default) or big-endian. All subsequent operations will use that order. * + * The flip() operation will reset the position to 0 and limit to the current position. This is useful for reading + * data from a buffer after it has been written. + * */ class ByteBuffer { public: + // Default constructor (compatibility with TEMPLATABLE_VALUE) + ByteBuffer() : ByteBuffer(std::vector()) {} /** * Create a new Bytebuffer with the given capacity */ - static ByteBuffer create(size_t capacity); + ByteBuffer(size_t capacity, Endian endianness = LITTLE) + : data_(std::vector(capacity)), endianness_(endianness), limit_(capacity){}; /** - * Wrap an existing vector in a Bytebufffer + * Wrap an existing vector in a ByteBufffer */ - static ByteBuffer wrap(std::vector data); + static ByteBuffer wrap(std::vector const &data, Endian endianness = LITTLE); /** - * Wrap an existing array in a Bytebufffer + * Wrap an existing array in a ByteBuffer. Note that this will create a copy of the data. */ - static ByteBuffer wrap(uint8_t *ptr, size_t len); + static ByteBuffer wrap(const uint8_t *ptr, size_t len, Endian endianness = LITTLE); + // Convenience functions to create a ByteBuffer from a value + static ByteBuffer wrap(uint8_t value); + static ByteBuffer wrap(uint16_t value, Endian endianness = LITTLE); + static ByteBuffer wrap(uint32_t value, Endian endianness = LITTLE); + static ByteBuffer wrap(uint64_t value, Endian endianness = LITTLE); + static ByteBuffer wrap(int8_t value) { return wrap(static_cast(value)); } + static ByteBuffer wrap(int16_t value, Endian endianness = LITTLE) { + return wrap(static_cast(value), endianness); + } + static ByteBuffer wrap(int32_t value, Endian endianness = LITTLE) { + return wrap(static_cast(value), endianness); + } + static ByteBuffer wrap(int64_t value, Endian endianness = LITTLE) { + return wrap(static_cast(value), endianness); + } + static ByteBuffer wrap(float value, Endian endianness = LITTLE); + static ByteBuffer wrap(double value, Endian endianness = LITTLE); + static ByteBuffer wrap(bool value) { return wrap(static_cast(value)); } + // Get an integral value from the buffer, increment position by length + uint64_t get_uint(size_t length); // Get one byte from the buffer, increment position by 1 uint8_t get_uint8(); // Get a 16 bit unsigned value, increment by 2 - uint16_t get_uint16(); + uint16_t get_uint16() { return static_cast(this->get_uint(sizeof(uint16_t))); }; // Get a 24 bit unsigned value, increment by 3 - uint32_t get_uint24(); + uint32_t get_uint24() { return static_cast(this->get_uint(3)); }; // Get a 32 bit unsigned value, increment by 4 - uint32_t get_uint32(); - // signed versions of the get functions - uint8_t get_int8() { return (int8_t) this->get_uint8(); }; - int16_t get_int16() { return (int16_t) this->get_uint16(); } + uint32_t get_uint32() { return static_cast(this->get_uint(sizeof(uint32_t))); }; + // Get a 64 bit unsigned value, increment by 8 + uint64_t get_uint64() { return this->get_uint(sizeof(uint64_t)); }; + // Signed versions of the get functions + uint8_t get_int8() { return static_cast(this->get_uint8()); }; + int16_t get_int16() { return static_cast(this->get_uint(sizeof(int16_t))); } uint32_t get_int24(); - int32_t get_int32() { return (int32_t) this->get_uint32(); } + int32_t get_int32() { return static_cast(this->get_uint(sizeof(int32_t))); } + int64_t get_int64() { return static_cast(this->get_uint(sizeof(int64_t))); } // Get a float value, increment by 4 float get_float(); + // Get a double value, increment by 8 + double get_double(); + // Get a bool value, increment by 1 + bool get_bool() { return this->get_uint8(); } + // Get vector of bytes, increment by length + std::vector get_vector(size_t length); - // put values into the buffer, increment the position accordingly + // Put values into the buffer, increment the position accordingly + // put any integral value, length represents the number of bytes + void put_uint(uint64_t value, size_t length); void put_uint8(uint8_t value); - void put_uint16(uint16_t value); - void put_uint24(uint32_t value); - void put_uint32(uint32_t value); + void put_uint16(uint16_t value) { this->put_uint(value, sizeof(uint16_t)); } + void put_uint24(uint32_t value) { this->put_uint(value, 3); } + void put_uint32(uint32_t value) { this->put_uint(value, sizeof(uint32_t)); } + void put_uint64(uint64_t value) { this->put_uint(value, sizeof(uint64_t)); } + // Signed versions of the put functions + void put_int8(int8_t value) { this->put_uint8(static_cast(value)); } + void put_int16(int32_t value) { this->put_uint(static_cast(value), sizeof(uint16_t)); } + void put_int24(int32_t value) { this->put_uint(static_cast(value), 3); } + void put_int32(int32_t value) { this->put_uint(static_cast(value), sizeof(uint32_t)); } + void put_int64(int64_t value) { this->put_uint(static_cast(value), sizeof(uint64_t)); } + // Extra put functions void put_float(float value); + void put_double(double value); + void put_bool(bool value) { this->put_uint8(value); } + void put_vector(const std::vector &value); inline size_t get_capacity() const { return this->data_.size(); } inline size_t get_position() const { return this->position_; } @@ -80,12 +128,12 @@ class ByteBuffer { // set limit to current position, postition to zero. Used when swapping from write to read operations. void flip(); // retrieve a pointer to the underlying data. - uint8_t *array() { return this->data_.data(); }; + std::vector get_data() { return this->data_; }; void rewind() { this->position_ = 0; } void reset() { this->position_ = this->mark_; } protected: - ByteBuffer(std::vector data) : data_(std::move(data)) { this->limit_ = this->get_capacity(); } + ByteBuffer(std::vector const &data) : data_(data), limit_(data.size()) {} std::vector data_; Endian endianness_{LITTLE}; size_t position_{0}; diff --git a/esphome/writer.py b/esphome/writer.py index c6111cbe3f..57435d3463 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -106,6 +106,8 @@ def storage_should_clean(old: StorageJSON, new: StorageJSON) -> bool: return True if old.build_path != new.build_path: return True + if old.loaded_integrations != new.loaded_integrations: + return True return False @@ -117,7 +119,9 @@ def update_storage_json(): return if storage_should_clean(old, new): - _LOGGER.info("Core config or version changed, cleaning build files...") + _LOGGER.info( + "Core config, version or integrations changed, cleaning build files..." + ) clean_build() new.save(path) diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 800d6eff27..1479ce7358 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -379,6 +379,7 @@ lvgl: format: "bar value %f" args: [x] - line: + id: lv_line_id align: center points: - 5, 5 @@ -387,7 +388,10 @@ lvgl: - 180, 60 - 240, 10 on_click: - lvgl.page.next: + - lvgl.widget.update: + id: lv_line_id + line_color: 0xFFFF + - lvgl.page.next: - switch: align: right_mid - checkbox: diff --git a/tests/components/pipsolar/test.esp32-ard.yaml b/tests/components/pipsolar/test.esp32-ard.yaml index fcd4575739..b7a7e0cbd9 100644 --- a/tests/components/pipsolar/test.esp32-ard.yaml +++ b/tests/components/pipsolar/test.esp32-ard.yaml @@ -220,6 +220,8 @@ switch: name: inverter0_output_source_priority_solar output_source_priority_battery: name: inverter0_output_source_priority_battery + output_source_priority_hybrid: + name: inverter0_output_source_priority_hybrid input_voltage_range: name: inverter0_input_voltage_range pv_ok_condition_for_parallel: diff --git a/tests/components/pipsolar/test.esp32-c3-ard.yaml b/tests/components/pipsolar/test.esp32-c3-ard.yaml index 12e9266343..83d7070669 100644 --- a/tests/components/pipsolar/test.esp32-c3-ard.yaml +++ b/tests/components/pipsolar/test.esp32-c3-ard.yaml @@ -220,6 +220,8 @@ switch: name: inverter0_output_source_priority_solar output_source_priority_battery: name: inverter0_output_source_priority_battery + output_source_priority_hybrid: + name: inverter0_output_source_priority_hybrid input_voltage_range: name: inverter0_input_voltage_range pv_ok_condition_for_parallel: diff --git a/tests/components/pipsolar/test.esp32-c3-idf.yaml b/tests/components/pipsolar/test.esp32-c3-idf.yaml index 12e9266343..83d7070669 100644 --- a/tests/components/pipsolar/test.esp32-c3-idf.yaml +++ b/tests/components/pipsolar/test.esp32-c3-idf.yaml @@ -220,6 +220,8 @@ switch: name: inverter0_output_source_priority_solar output_source_priority_battery: name: inverter0_output_source_priority_battery + output_source_priority_hybrid: + name: inverter0_output_source_priority_hybrid input_voltage_range: name: inverter0_input_voltage_range pv_ok_condition_for_parallel: diff --git a/tests/components/pipsolar/test.esp32-idf.yaml b/tests/components/pipsolar/test.esp32-idf.yaml index fcd4575739..b7a7e0cbd9 100644 --- a/tests/components/pipsolar/test.esp32-idf.yaml +++ b/tests/components/pipsolar/test.esp32-idf.yaml @@ -220,6 +220,8 @@ switch: name: inverter0_output_source_priority_solar output_source_priority_battery: name: inverter0_output_source_priority_battery + output_source_priority_hybrid: + name: inverter0_output_source_priority_hybrid input_voltage_range: name: inverter0_input_voltage_range pv_ok_condition_for_parallel: diff --git a/tests/components/pipsolar/test.esp8266-ard.yaml b/tests/components/pipsolar/test.esp8266-ard.yaml index 12e9266343..83d7070669 100644 --- a/tests/components/pipsolar/test.esp8266-ard.yaml +++ b/tests/components/pipsolar/test.esp8266-ard.yaml @@ -220,6 +220,8 @@ switch: name: inverter0_output_source_priority_solar output_source_priority_battery: name: inverter0_output_source_priority_battery + output_source_priority_hybrid: + name: inverter0_output_source_priority_hybrid input_voltage_range: name: inverter0_input_voltage_range pv_ok_condition_for_parallel: diff --git a/tests/components/pipsolar/test.rp2040-ard.yaml b/tests/components/pipsolar/test.rp2040-ard.yaml index 12e9266343..83d7070669 100644 --- a/tests/components/pipsolar/test.rp2040-ard.yaml +++ b/tests/components/pipsolar/test.rp2040-ard.yaml @@ -220,6 +220,8 @@ switch: name: inverter0_output_source_priority_solar output_source_priority_battery: name: inverter0_output_source_priority_battery + output_source_priority_hybrid: + name: inverter0_output_source_priority_hybrid input_voltage_range: name: inverter0_input_voltage_range pv_ok_condition_for_parallel: