Merge branch 'dev' into add-graphical-layout-system

This commit is contained in:
Jesse Hills 2024-08-08 14:42:57 +12:00 committed by GitHub
commit 9bf17a422b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
76 changed files with 1286 additions and 356 deletions

View file

@ -277,6 +277,7 @@ esphome/components/nfc/* @jesserockz @kbx81
esphome/components/noblex/* @AGalfra
esphome/components/number/* @esphome/core
esphome/components/one_wire/* @ssieb
esphome/components/online_image/* @guillempages
esphome/components/ota/* @esphome/core
esphome/components/output/* @esphome/core
esphome/components/pca6416a/* @Mat931

View file

@ -1,10 +1,8 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
from esphome import automation, core
from esphome.automation import Condition, maybe_simple_id
import esphome.codegen as cg
from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import (
CONF_DELAY,
CONF_DEVICE_CLASS,
@ -16,6 +14,7 @@ from esphome.const import (
CONF_INVERTED,
CONF_MAX_LENGTH,
CONF_MIN_LENGTH,
CONF_MQTT_ID,
CONF_ON_CLICK,
CONF_ON_DOUBLE_CLICK,
CONF_ON_MULTI_CLICK,
@ -26,7 +25,6 @@ from esphome.const import (
CONF_STATE,
CONF_TIMING,
CONF_TRIGGER_ID,
CONF_MQTT_ID,
CONF_WEB_SERVER_ID,
DEVICE_CLASS_BATTERY,
DEVICE_CLASS_BATTERY_CHARGING,
@ -59,6 +57,8 @@ from esphome.const import (
DEVICE_CLASS_WINDOW,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
from esphome.util import Registry
CODEOWNERS = ["@esphome/core"]

View file

@ -1,16 +1,16 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.automation import maybe_simple_id
import esphome.codegen as cg
from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import (
CONF_DEVICE_CLASS,
CONF_ENTITY_CATEGORY,
CONF_ICON,
CONF_ID,
CONF_MQTT_ID,
CONF_ON_PRESS,
CONF_TRIGGER_ID,
CONF_MQTT_ID,
CONF_WEB_SERVER_ID,
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_IDENTIFY,
@ -18,8 +18,8 @@ from esphome.const import (
DEVICE_CLASS_UPDATE,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity
from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@esphome/core"]
IS_PLATFORM_COMPONENT = True

View file

@ -1,8 +1,7 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.cpp_helpers import setup_entity
from esphome import automation
import esphome.codegen as cg
from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import (
CONF_ACTION_STATE_TOPIC,
CONF_AWAY,
@ -21,6 +20,7 @@ from esphome.const import (
CONF_MODE,
CONF_MODE_COMMAND_TOPIC,
CONF_MODE_STATE_TOPIC,
CONF_MQTT_ID,
CONF_ON_CONTROL,
CONF_ON_STATE,
CONF_PRESET,
@ -33,20 +33,20 @@ from esphome.const import (
CONF_TARGET_HUMIDITY_STATE_TOPIC,
CONF_TARGET_TEMPERATURE,
CONF_TARGET_TEMPERATURE_COMMAND_TOPIC,
CONF_TARGET_TEMPERATURE_STATE_TOPIC,
CONF_TARGET_TEMPERATURE_HIGH,
CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC,
CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC,
CONF_TARGET_TEMPERATURE_LOW,
CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC,
CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC,
CONF_TARGET_TEMPERATURE_STATE_TOPIC,
CONF_TEMPERATURE_STEP,
CONF_TRIGGER_ID,
CONF_VISUAL,
CONF_MQTT_ID,
CONF_WEB_SERVER_ID,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity
IS_PLATFORM_COMPONENT = True

View file

@ -141,7 +141,7 @@ struct ClimateDeviceRestoreState {
float target_temperature_low;
float target_temperature_high;
};
};
} __attribute__((packed));
float target_humidity;
/// Convert this struct to a climate call that can be performed.

View file

@ -1,23 +1,23 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.automation import maybe_simple_id, Condition
from esphome.automation import Condition, maybe_simple_id
import esphome.codegen as cg
from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import (
CONF_ID,
CONF_DEVICE_CLASS,
CONF_STATE,
CONF_ID,
CONF_MQTT_ID,
CONF_ON_OPEN,
CONF_POSITION,
CONF_POSITION_COMMAND_TOPIC,
CONF_POSITION_STATE_TOPIC,
CONF_STATE,
CONF_STOP,
CONF_TILT,
CONF_TILT_COMMAND_TOPIC,
CONF_TILT_STATE_TOPIC,
CONF_STOP,
CONF_MQTT_ID,
CONF_WEB_SERVER_ID,
CONF_TRIGGER_ID,
CONF_WEB_SERVER_ID,
DEVICE_CLASS_AWNING,
DEVICE_CLASS_BLIND,
DEVICE_CLASS_CURTAIN,

View file

@ -5,6 +5,7 @@ namespace cst226 {
void CST226Touchscreen::setup() {
esph_log_config(TAG, "Setting up CST226 Touchscreen...");
if (this->reset_pin_ != nullptr) {
this->reset_pin_->setup();
this->reset_pin_->digital_write(true);
delay(5);
@ -12,6 +13,9 @@ void CST226Touchscreen::setup() {
delay(5);
this->reset_pin_->digital_write(true);
this->set_timeout(30, [this] { this->continue_setup_(); });
} else {
this->continue_setup_();
}
}
void CST226Touchscreen::update_touches() {

View file

@ -35,7 +35,7 @@ class CST226Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice
void continue_setup_();
InternalGPIOPin *interrupt_pin_{};
GPIOPin *reset_pin_{NULL_PIN};
GPIOPin *reset_pin_{};
uint8_t chip_id_{};
bool setup_complete_{};
};

View file

@ -1,32 +1,30 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.components import mqtt, web_server, time
import esphome.codegen as cg
from esphome.components import mqtt, time, web_server
import esphome.config_validation as cv
from esphome.const import (
CONF_DATE,
CONF_DATETIME,
CONF_DAY,
CONF_HOUR,
CONF_ID,
CONF_MINUTE,
CONF_MONTH,
CONF_MQTT_ID,
CONF_ON_TIME,
CONF_ON_VALUE,
CONF_SECOND,
CONF_TIME,
CONF_TIME_ID,
CONF_TRIGGER_ID,
CONF_TYPE,
CONF_MQTT_ID,
CONF_WEB_SERVER_ID,
CONF_DATE,
CONF_DATETIME,
CONF_TIME,
CONF_YEAR,
CONF_MONTH,
CONF_DAY,
CONF_SECOND,
CONF_HOUR,
CONF_MINUTE,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@rfdarter", "@jesserockz"]
DEPENDENCIES = ["time"]

View file

@ -1,23 +1,26 @@
import re
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation, core
from esphome.automation import maybe_simple_id
import esphome.codegen as cg
from esphome.components.number import Number
from esphome.components.select import Select
from esphome.components.switch import Switch
import esphome.config_validation as cv
from esphome.const import (
CONF_ID,
CONF_TYPE,
CONF_TRIGGER_ID,
CONF_ON_VALUE,
CONF_ACTIVE,
CONF_COMMAND,
CONF_CUSTOM,
CONF_NUMBER,
CONF_FORMAT,
CONF_ID,
CONF_ITEMS,
CONF_MODE,
CONF_ACTIVE,
CONF_NUMBER,
CONF_ON_VALUE,
CONF_TEXT,
CONF_TRIGGER_ID,
CONF_TYPE,
)
from esphome.automation import maybe_simple_id
from esphome.components.select import Select
from esphome.components.number import Number
from esphome.components.switch import Switch
CODEOWNERS = ["@numo68"]
@ -29,10 +32,8 @@ CONF_JOYSTICK = "joystick"
CONF_LABEL = "label"
CONF_MENU = "menu"
CONF_BACK = "back"
CONF_TEXT = "text"
CONF_SELECT = "select"
CONF_SWITCH = "switch"
CONF_ITEMS = "items"
CONF_ON_TEXT = "on_text"
CONF_OFF_TEXT = "off_text"
CONF_VALUE_LAMBDA = "value_lambda"

View file

@ -1,24 +1,24 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
import esphome.codegen as cg
from esphome.components import mqtt
import esphome.config_validation as cv
from esphome.const import (
CONF_DEVICE_CLASS,
CONF_ENTITY_CATEGORY,
CONF_EVENT_TYPE,
CONF_ICON,
CONF_ID,
CONF_MQTT_ID,
CONF_ON_EVENT,
CONF_TRIGGER_ID,
CONF_MQTT_ID,
CONF_EVENT_TYPE,
DEVICE_CLASS_BUTTON,
DEVICE_CLASS_DOORBELL,
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_MOTION,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity
from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@nohat"]
IS_PLATFORM_COMPONENT = True

View file

@ -1,31 +1,31 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.automation import maybe_simple_id
import esphome.codegen as cg
from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import (
CONF_DIRECTION,
CONF_ID,
CONF_MQTT_ID,
CONF_WEB_SERVER_ID,
CONF_OSCILLATING,
CONF_OSCILLATION_COMMAND_TOPIC,
CONF_OSCILLATION_STATE_TOPIC,
CONF_SPEED,
CONF_SPEED_LEVEL_COMMAND_TOPIC,
CONF_SPEED_LEVEL_STATE_TOPIC,
CONF_SPEED_COMMAND_TOPIC,
CONF_SPEED_STATE_TOPIC,
CONF_OFF_SPEED_CYCLE,
CONF_ON_DIRECTION_SET,
CONF_ON_OSCILLATING_SET,
CONF_ON_PRESET_SET,
CONF_ON_SPEED_SET,
CONF_ON_STATE,
CONF_ON_TURN_OFF,
CONF_ON_TURN_ON,
CONF_ON_PRESET_SET,
CONF_TRIGGER_ID,
CONF_DIRECTION,
CONF_OSCILLATING,
CONF_OSCILLATION_COMMAND_TOPIC,
CONF_OSCILLATION_STATE_TOPIC,
CONF_RESTORE_MODE,
CONF_SPEED,
CONF_SPEED_COMMAND_TOPIC,
CONF_SPEED_LEVEL_COMMAND_TOPIC,
CONF_SPEED_LEVEL_STATE_TOPIC,
CONF_SPEED_STATE_TOPIC,
CONF_TRIGGER_ID,
CONF_WEB_SERVER_ID,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity

View file

@ -10,12 +10,12 @@ import esphome.config_validation as cv
from esphome.const import (
CONF_BACKGROUND_COLOR,
CONF_DISPLAY,
CONF_FONT,
CONF_FOREGROUND_COLOR,
CONF_ID,
CONF_TRIGGER_ID,
)
CONF_FONT = "font"
CONF_MENU_ITEM_VALUE = "menu_item_value"
CONF_ON_REDRAW = "on_redraw"

View file

@ -39,8 +39,8 @@ bool HX711Sensor::read_sensor_(uint32_t *result) {
return false;
}
this->status_clear_warning();
uint32_t data = 0;
bool final_dout;
{
InterruptLock lock;
@ -59,8 +59,17 @@ bool HX711Sensor::read_sensor_(uint32_t *result) {
this->sck_pin_->digital_write(false);
delayMicroseconds(1);
}
final_dout = this->dout_pin_->digital_read();
}
if (!final_dout) {
ESP_LOGW(TAG, "HX711 DOUT pin not high after reading (data 0x%" PRIx32 ")!", data);
this->status_set_warning();
return false;
}
this->status_clear_warning();
if (data & 0x800000ULL) {
data |= 0xFF000000ULL;
}

View file

@ -233,6 +233,7 @@ void I2SAudioSpeaker::loop() {
switch (this->state_) {
case speaker::STATE_STARTING:
this->start_();
[[fallthrough]];
case speaker::STATE_RUNNING:
case speaker::STATE_STOPPING:
this->watch_();

View file

@ -1,8 +1,9 @@
import esphome.codegen as cg
import esphome.config_validation as cv
import esphome.automation as auto
import esphome.codegen as cg
from esphome.components import mqtt, power_supply, web_server
import esphome.config_validation as cv
from esphome.const import (
CONF_COLD_WHITE_COLOR_TEMPERATURE,
CONF_COLOR_CORRECT,
CONF_DEFAULT_TRANSITION_LENGTH,
CONF_EFFECTS,
@ -10,36 +11,36 @@ from esphome.const import (
CONF_GAMMA_CORRECT,
CONF_ID,
CONF_MQTT_ID,
CONF_WEB_SERVER_ID,
CONF_POWER_SUPPLY,
CONF_RESTORE_MODE,
CONF_ON_STATE,
CONF_ON_TURN_OFF,
CONF_ON_TURN_ON,
CONF_ON_STATE,
CONF_POWER_SUPPLY,
CONF_RESTORE_MODE,
CONF_TRIGGER_ID,
CONF_COLD_WHITE_COLOR_TEMPERATURE,
CONF_WARM_WHITE_COLOR_TEMPERATURE,
CONF_WEB_SERVER_ID,
)
from esphome.core import coroutine_with_priority
from esphome.cpp_helpers import setup_entity
from .automation import light_control_to_code # noqa
from .effects import (
validate_effects,
ADDRESSABLE_EFFECTS,
BINARY_EFFECTS,
EFFECTS_REGISTRY,
MONOCHROMATIC_EFFECTS,
RGB_EFFECTS,
ADDRESSABLE_EFFECTS,
EFFECTS_REGISTRY,
validate_effects,
)
from .types import ( # noqa
LightState,
AddressableLightState,
light_ns,
LightOutput,
AddressableLight,
LightTurnOnTrigger,
LightTurnOffTrigger,
AddressableLightState,
LightOutput,
LightState,
LightStateTrigger,
LightTurnOffTrigger,
LightTurnOnTrigger,
light_ns,
)
CODEOWNERS = ["@esphome/core"]

View file

@ -1,14 +1,14 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.automation import Condition, maybe_simple_id
import esphome.codegen as cg
from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import (
CONF_ID,
CONF_MQTT_ID,
CONF_ON_LOCK,
CONF_ON_UNLOCK,
CONF_TRIGGER_ID,
CONF_MQTT_ID,
CONF_WEB_SERVER_ID,
)
from esphome.core import CORE, coroutine_with_priority

View file

@ -1,9 +1,21 @@
import re
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.automation import LambdaAction
import esphome.codegen as cg
from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant
from esphome.components.esp32.const import (
VARIANT_ESP32,
VARIANT_ESP32C2,
VARIANT_ESP32C3,
VARIANT_ESP32C6,
VARIANT_ESP32H2,
VARIANT_ESP32S2,
VARIANT_ESP32S3,
)
from esphome.components.libretiny import get_libretiny_component, get_libretiny_family
from esphome.components.libretiny.const import COMPONENT_BK72XX, COMPONENT_RTL87XX
import esphome.config_validation as cv
from esphome.const import (
CONF_ARGS,
CONF_BAUD_RATE,
@ -18,27 +30,12 @@ from esphome.const import (
CONF_TRIGGER_ID,
CONF_TX_BUFFER_SIZE,
PLATFORM_BK72XX,
PLATFORM_RTL87XX,
PLATFORM_ESP32,
PLATFORM_ESP8266,
PLATFORM_RP2040,
PLATFORM_RTL87XX,
)
from esphome.core import CORE, EsphomeError, Lambda, coroutine_with_priority
from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant
from esphome.components.esp32.const import (
VARIANT_ESP32,
VARIANT_ESP32S2,
VARIANT_ESP32C3,
VARIANT_ESP32S3,
VARIANT_ESP32C2,
VARIANT_ESP32C6,
VARIANT_ESP32H2,
)
from esphome.components.libretiny import get_libretiny_component, get_libretiny_family
from esphome.components.libretiny.const import (
COMPONENT_BK72XX,
COMPONENT_RTL87XX,
)
CODEOWNERS = ["@esphome/core"]
logger_ns = cg.esphome_ns.namespace("logger")

View file

@ -23,9 +23,9 @@ from esphome.helpers import write_file_if_changed
from . import defines as df, helpers, lv_validation as lvalid
from .automation import disp_update, update_to_code
from .defines import CONF_SKIP
from .encoders import ENCODERS_CONFIG, encoders_to_code
from .lv_validation import lv_bool, lv_images_used
from .lvcode import LvContext, LvglComponent
from .rotary_encoders import ROTARY_ENCODER_CONFIG, rotary_encoders_to_code
from .schemas import (
DISP_BG_SCHEMA,
FLEX_OBJ_SCHEMA,
@ -256,7 +256,7 @@ async def to_code(config):
async with LvContext(lv_component):
await touchscreens_to_code(lv_component, config)
await rotary_encoders_to_code(lv_component, config)
await encoders_to_code(lv_component, config)
await theme_to_code(config)
await styles_to_code(config)
await set_obj_properties(lv_scr_act, config)
@ -336,7 +336,7 @@ CONFIG_SCHEMA = (
{cv.Optional(name): obj_schema(w) for name, w in WIDGET_TYPES.items()}
),
cv.GenerateID(df.CONF_TOUCHSCREENS): touchscreen_schema,
cv.GenerateID(df.CONF_ROTARY_ENCODERS): ROTARY_ENCODER_CONFIG,
cv.GenerateID(df.CONF_ENCODERS): ENCODERS_CONFIG,
}
)
.extend(DISP_BG_SCHEMA)

View file

@ -5,6 +5,7 @@ Constants already defined in esphome.const are not duplicated here and must be i
"""
from esphome import codegen as cg, config_validation as cv
from esphome.const import CONF_ITEMS
from esphome.core import ID, Lambda
from esphome.cpp_generator import MockObj
from esphome.cpp_types import uint32
@ -115,7 +116,6 @@ CONF_SCROLLBAR = "scrollbar"
CONF_INDICATOR = "indicator"
CONF_KNOB = "knob"
CONF_SELECTED = "selected"
CONF_ITEMS = "items"
CONF_TICKS = "ticks"
CONF_CURSOR = "cursor"
CONF_TEXTAREA_PLACEHOLDER = "textarea_placeholder"
@ -388,6 +388,7 @@ CONF_DEFAULT = "default"
CONF_DEFAULT_FONT = "default_font"
CONF_DIR = "dir"
CONF_DISPLAYS = "displays"
CONF_ENCODERS = "encoders"
CONF_END_ANGLE = "end_angle"
CONF_END_VALUE = "end_value"
CONF_ENTER_BUTTON = "enter_button"
@ -441,7 +442,6 @@ CONF_RECOLOR = "recolor"
CONF_RIGHT_BUTTON = "right_button"
CONF_ROLLOVER = "rollover"
CONF_ROOT_BACK_BTN = "root_back_btn"
CONF_ROTARY_ENCODERS = "rotary_encoders"
CONF_ROWS = "rows"
CONF_SCALE_LINES = "scale_lines"
CONF_SCROLLBAR_MODE = "scrollbar_mode"
@ -460,7 +460,6 @@ CONF_SKIP = "skip"
CONF_SYMBOL = "symbol"
CONF_TAB_ID = "tab_id"
CONF_TABS = "tabs"
CONF_TEXT = "text"
CONF_TILE = "tile"
CONF_TILE_ID = "tile_id"
CONF_TILES = "tiles"

View file

@ -5,25 +5,26 @@ import esphome.config_validation as cv
from esphome.const import CONF_GROUP, CONF_ID, CONF_SENSOR
from .defines import (
CONF_ENCODERS,
CONF_ENTER_BUTTON,
CONF_LEFT_BUTTON,
CONF_LONG_PRESS_REPEAT_TIME,
CONF_LONG_PRESS_TIME,
CONF_RIGHT_BUTTON,
CONF_ROTARY_ENCODERS,
)
from .helpers import lvgl_components_required
from .lvcode import lv, lv_add, lv_expr
from .helpers import lvgl_components_required, requires_component
from .lvcode import lv, lv_add, lv_assign, lv_expr, lv_Pvariable
from .schemas import ENCODER_SCHEMA
from .types import lv_indev_type_t
from .widgets import add_group
from .types import lv_group_t, lv_indev_type_t
ROTARY_ENCODER_CONFIG = cv.ensure_list(
ENCODERS_CONFIG = cv.ensure_list(
ENCODER_SCHEMA.extend(
{
cv.Required(CONF_ENTER_BUTTON): cv.use_id(BinarySensor),
cv.Required(CONF_SENSOR): cv.Any(
cv.use_id(RotaryEncoderSensor),
cv.All(
cv.use_id(RotaryEncoderSensor), requires_component("rotary_encoder")
),
cv.Schema(
{
cv.Required(CONF_LEFT_BUTTON): cv.use_id(BinarySensor),
@ -36,10 +37,9 @@ ROTARY_ENCODER_CONFIG = cv.ensure_list(
)
async def rotary_encoders_to_code(var, config):
for enc_conf in config.get(CONF_ROTARY_ENCODERS, ()):
async def encoders_to_code(var, config):
for enc_conf in config.get(CONF_ENCODERS, ()):
lvgl_components_required.add("KEY_LISTENER")
lvgl_components_required.add("ROTARY_ENCODER")
lpt = enc_conf[CONF_LONG_PRESS_TIME].total_milliseconds
lprt = enc_conf[CONF_LONG_PRESS_REPEAT_TIME].total_milliseconds
listener = cg.new_Pvariable(
@ -57,7 +57,9 @@ async def rotary_encoders_to_code(var, config):
lv_add(listener.set_sensor(sensor_config))
b_sensor = await cg.get_variable(enc_conf[CONF_ENTER_BUTTON])
cg.add(listener.set_enter_button(b_sensor))
if group := add_group(enc_conf.get(CONF_GROUP)):
if group := enc_conf.get(CONF_GROUP):
group = lv_Pvariable(lv_group_t, group)
lv_assign(group, lv_expr.group_create())
lv.indev_set_group(lv_expr.indev_drv_register(listener.get_drv()), group)
else:
lv.indev_drv_register(listener.get_drv())

View file

@ -127,7 +127,7 @@ void LVTouchListener::update(const touchscreen::TouchPoints_t &tpoints) {
}
#endif // USE_LVGL_TOUCHSCREEN
#ifdef USE_LVGL_ROTARY_ENCODER
#ifdef USE_LVGL_KEY_LISTENER
LVEncoderListener::LVEncoderListener(lv_indev_type_t type, uint16_t lpt, uint16_t lprt) {
lv_indev_drv_init(&this->drv_);
this->drv_.type = type;
@ -143,7 +143,7 @@ LVEncoderListener::LVEncoderListener(lv_indev_type_t type, uint16_t lpt, uint16_
data->continue_reading = false;
};
}
#endif // USE_LVGL_ROTARY_ENCODER
#endif // USE_LVGL_KEY_LISTENER
#ifdef USE_LVGL_BUTTONMATRIX
void LvButtonMatrixType::set_obj(lv_obj_t *lv_obj) {

View file

@ -1,6 +1,13 @@
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_LVGL_BINARY_SENSOR
#include "esphome/components/binary_sensor/binary_sensor.h"
#endif // USE_LVGL_BINARY_SENSOR
#ifdef USE_LVGL_ROTARY_ENCODER
#include "esphome/components/rotary_encoder/rotary_encoder.h"
#endif // USE_LVGL_ROTARY_ENCODER
// required for clang-tidy
#ifndef LV_CONF_H
#define LV_CONF_SKIP 1 // NOLINT
@ -12,12 +19,7 @@
#include "esphome/core/log.h"
#include <lvgl.h>
#include <vector>
#ifdef USE_LVGL_ROTARY_ENCODER
#include "esphome/components/binary_sensor/binary_sensor.h"
#include "esphome/components/rotary_encoder/rotary_encoder.h"
#endif // USE_LVGL_ROTARY_ENCODER
#include <map>
#ifdef USE_LVGL_IMAGE
#include "esphome/components/image/image.h"
#endif // USE_LVGL_IMAGE
@ -202,7 +204,7 @@ class LVTouchListener : public touchscreen::TouchListener, public Parented<LvglC
};
#endif // USE_LVGL_TOUCHSCREEN
#ifdef USE_LVGL_ROTARY_ENCODER
#ifdef USE_LVGL_KEY_LISTENER
class LVEncoderListener : public Parented<LvglComponent> {
public:
LVEncoderListener(lv_indev_type_t type, uint16_t lpt, uint16_t lprt);
@ -218,9 +220,11 @@ class LVEncoderListener : public Parented<LvglComponent> {
enter_button->add_on_state_callback([this](bool state) { this->event(LV_KEY_ENTER, state); });
}
#ifdef USE_LVGL_ROTARY_ENCODER
void set_sensor(rotary_encoder::RotaryEncoderSensor *sensor) {
sensor->register_listener([this](int32_t count) { this->set_count(count); });
}
#endif // USE_LVGL_ROTARY_ENCODER
void event(int key, bool pressed) {
if (!this->parent_->is_paused()) {
@ -243,7 +247,8 @@ class LVEncoderListener : public Parented<LvglComponent> {
int32_t last_count_{};
int key_{};
};
#endif // USE_LVGL_ROTARY_ENCODER
#endif // USE_LVGL_KEY_LISTENER
#ifdef USE_LVGL_BUTTONMATRIX
class LvButtonMatrixType : public key_provider::KeyProvider, public LvCompound {
public:

View file

@ -7,6 +7,7 @@ from esphome.const import (
CONF_ID,
CONF_ON_VALUE,
CONF_STATE,
CONF_TEXT,
CONF_TRIGGER_ID,
CONF_TYPE,
)
@ -15,9 +16,9 @@ from esphome.schema_extractors import SCHEMA_EXTRACT
from . import defines as df, lv_validation as lvalid, types as ty
from .helpers import add_lv_use, requires_component, validate_printf
from .lv_validation import id_name, lv_color, lv_font, lv_image
from .lv_validation import lv_color, lv_font, lv_image
from .lvcode import LvglComponent
from .types import WidgetType
from .types import WidgetType, lv_group_t
# this will be populated later, in __init__.py to avoid circular imports.
WIDGET_TYPES: dict = {}
@ -25,7 +26,7 @@ WIDGET_TYPES: dict = {}
# A schema for text properties
TEXT_SCHEMA = cv.Schema(
{
cv.Optional(df.CONF_TEXT): cv.Any(
cv.Optional(CONF_TEXT): cv.Any(
cv.All(
cv.Schema(
{
@ -60,7 +61,7 @@ ENCODER_SCHEMA = cv.Schema(
cv.GenerateID(): cv.All(
cv.declare_id(ty.LVEncoderListener), requires_component("binary_sensor")
),
cv.Optional(CONF_GROUP): lvalid.id_name,
cv.Optional(CONF_GROUP): cv.declare_id(lv_group_t),
cv.Optional(df.CONF_LONG_PRESS_TIME, default="400ms"): PRESS_TIME,
cv.Optional(df.CONF_LONG_PRESS_REPEAT_TIME, default="100ms"): PRESS_TIME,
}
@ -248,7 +249,7 @@ def obj_schema(widget_type: WidgetType):
cv.Schema(
{
cv.Optional(CONF_STATE): SET_STATE_SCHEMA,
cv.Optional(CONF_GROUP): id_name,
cv.Optional(CONF_GROUP): cv.use_id(lv_group_t),
}
)
)
@ -330,7 +331,7 @@ DISP_BG_SCHEMA = cv.Schema(
# A style schema that can include text
STYLED_TEXT_SCHEMA = cv.maybe_simple_value(
STYLE_SCHEMA.extend(TEXT_SCHEMA), key=df.CONF_TEXT
STYLE_SCHEMA.extend(TEXT_SCHEMA), key=CONF_TEXT
)
# For use by platform components

View file

@ -1,10 +1,10 @@
import sys
from esphome import automation, codegen as cg
from esphome.const import CONF_MAX_VALUE, CONF_MIN_VALUE, CONF_VALUE
from esphome.const import CONF_MAX_VALUE, CONF_MIN_VALUE, CONF_TEXT, CONF_VALUE
from esphome.cpp_generator import MockObj, MockObjClass
from .defines import CONF_TEXT, lvgl_ns
from .defines import lvgl_ns
from .lvcode import lv_expr

View file

@ -6,7 +6,7 @@ from esphome.config_validation import Invalid
from esphome.const import CONF_GROUP, CONF_ID, CONF_STATE, CONF_TYPE
from esphome.core import ID, TimePeriod
from esphome.coroutine import FakeAwaitable
from esphome.cpp_generator import AssignmentExpression, CallExpression, MockObj
from esphome.cpp_generator import CallExpression, MockObj
from ..defines import (
CONF_DEFAULT,
@ -44,15 +44,7 @@ from ..lvcode import (
lv_Pvariable,
)
from ..schemas import ALL_STYLES, STYLE_REMAP, WIDGET_TYPES
from ..types import (
LV_STATE,
LvType,
WidgetType,
lv_coord_t,
lv_group_t,
lv_obj_t,
lv_obj_t_ptr,
)
from ..types import LV_STATE, LvType, WidgetType, lv_coord_t, lv_obj_t, lv_obj_t_ptr
EVENT_LAMB = "event_lamb__"
@ -317,7 +309,8 @@ async def set_obj_properties(w: Widget, config):
value = await ALL_STYLES[prop].process(value)
prop_r = STYLE_REMAP.get(prop, prop)
w.set_style(prop_r, value, lv_state)
if group := add_group(config.get(CONF_GROUP)):
if group := config.get(CONF_GROUP):
group = await cg.get_variable(group)
lv.group_add_obj(group, w.obj)
flag_clr = set()
flag_set = set()
@ -404,20 +397,3 @@ async def widget_to_code(w_cnfig, w_type: WidgetType, parent):
lv_scr_act_spec = LvScrActType()
lv_scr_act = Widget.create(None, literal("lv_scr_act()"), lv_scr_act_spec, {})
lv_groups = {} # Widget group names
def add_group(name):
if name is None:
return None
fullname = f"lv_esp_group_{name}"
if name not in lv_groups:
gid = ID(fullname, True, type=lv_group_t.operator("ptr"))
lv_add(
AssignmentExpression(
type_=gid.type, modifier="", name=fullname, rhs=lv_expr.group_create()
)
)
lv_groups[name] = literal(fullname)
return lv_groups[name]

View file

@ -2,7 +2,7 @@ from esphome import automation
import esphome.codegen as cg
from esphome.components.key_provider import KeyProvider
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_WIDTH
from esphome.const import CONF_ID, CONF_ITEMS, CONF_TEXT, CONF_WIDTH
from esphome.cpp_generator import MockObj
from ..automation import action_to_code
@ -10,13 +10,11 @@ from ..defines import (
BUTTONMATRIX_CTRLS,
CONF_BUTTONS,
CONF_CONTROL,
CONF_ITEMS,
CONF_KEY_CODE,
CONF_MAIN,
CONF_ONE_CHECKED,
CONF_ROWS,
CONF_SELECTED,
CONF_TEXT,
)
from ..helpers import lvgl_components_required
from ..lv_validation import key_code, lv_bool

View file

@ -1,4 +1,6 @@
from ..defines import CONF_INDICATOR, CONF_MAIN, CONF_TEXT
from esphome.const import CONF_TEXT
from ..defines import CONF_INDICATOR, CONF_MAIN
from ..lv_validation import lv_text
from ..lvcode import lv
from ..schemas import TEXT_SCHEMA

View file

@ -1,9 +1,9 @@
from esphome.components.key_provider import KeyProvider
import esphome.config_validation as cv
from esphome.const import CONF_MODE
from esphome.const import CONF_ITEMS, CONF_MODE
from esphome.cpp_types import std_string
from ..defines import CONF_ITEMS, CONF_MAIN, KEYBOARD_MODES, literal
from ..defines import CONF_MAIN, KEYBOARD_MODES, literal
from ..helpers import add_lv_use, lvgl_components_required
from ..types import LvCompound, LvType
from . import Widget, WidgetType, get_widgets

View file

@ -1,4 +1,5 @@
import esphome.config_validation as cv
from esphome.const import CONF_TEXT
from ..defines import (
CONF_LONG_MODE,
@ -6,7 +7,6 @@ from ..defines import (
CONF_RECOLOR,
CONF_SCROLLBAR,
CONF_SELECTED,
CONF_TEXT,
LV_LONG_MODES,
)
from ..lv_validation import lv_bool, lv_text

View file

@ -1,5 +1,5 @@
from esphome import config_validation as cv
from esphome.const import CONF_BUTTON, CONF_ID
from esphome.const import CONF_BUTTON, CONF_ID, CONF_TEXT
from esphome.core import ID
from esphome.cpp_generator import new_Pvariable, static_const_array
from esphome.cpp_types import nullptr
@ -9,7 +9,6 @@ from ..defines import (
CONF_BUTTONS,
CONF_CLOSE_BUTTON,
CONF_MSGBOXES,
CONF_TEXT,
CONF_TITLE,
TYPE_FLEX,
literal,

View file

@ -1,5 +1,5 @@
import esphome.config_validation as cv
from esphome.const import CONF_MAX_LENGTH
from esphome.const import CONF_MAX_LENGTH, CONF_TEXT
from ..defines import (
CONF_ACCEPTED_CHARS,
@ -10,7 +10,6 @@ from ..defines import (
CONF_PLACEHOLDER_TEXT,
CONF_SCROLLBAR,
CONF_SELECTED,
CONF_TEXT,
CONF_TEXTAREA_PLACEHOLDER,
)
from ..lv_validation import lv_bool, lv_int, lv_text

View file

@ -1,6 +1,6 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, spi
import esphome.config_validation as cv
from esphome.const import (
CONF_MAINS_FILTER,
DEVICE_CLASS_TEMPERATURE,
@ -15,8 +15,8 @@ MAX31856Sensor = max31856_ns.class_(
MAX31865ConfigFilter = max31856_ns.enum("MAX31856ConfigFilter")
FILTER = {
"50HZ": MAX31865ConfigFilter.FILTER_50HZ,
"60HZ": MAX31865ConfigFilter.FILTER_60HZ,
50: MAX31865ConfigFilter.FILTER_50HZ,
60: MAX31865ConfigFilter.FILTER_60HZ,
}
CONFIG_SCHEMA = (
@ -29,8 +29,8 @@ CONFIG_SCHEMA = (
)
.extend(
{
cv.Optional(CONF_MAINS_FILTER, default="60HZ"): cv.enum(
FILTER, upper=True, space=""
cv.Optional(CONF_MAINS_FILTER, default="60Hz"): cv.All(
cv.frequency, cv.enum(FILTER, int=True)
),
}
)

View file

@ -1,20 +1,18 @@
from esphome import automation
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.automation import maybe_simple_id
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.const import (
CONF_ID,
CONF_ON_IDLE,
CONF_ON_STATE,
CONF_TRIGGER_ID,
CONF_VOLUME,
CONF_ON_IDLE,
)
from esphome.core import CORE
from esphome.coroutine import coroutine_with_priority
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@jesserockz"]
IS_PLATFORM_COMPONENT = True

View file

@ -1,24 +1,23 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.components import mqtt
from esphome.components import web_server
import esphome.codegen as cg
from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import (
CONF_ABOVE,
CONF_BELOW,
CONF_CYCLE,
CONF_DEVICE_CLASS,
CONF_ENTITY_CATEGORY,
CONF_ID,
CONF_ICON,
CONF_ID,
CONF_MODE,
CONF_MQTT_ID,
CONF_ON_VALUE,
CONF_ON_VALUE_RANGE,
CONF_OPERATION,
CONF_TRIGGER_ID,
CONF_UNIT_OF_MEASUREMENT,
CONF_MQTT_ID,
CONF_VALUE,
CONF_OPERATION,
CONF_CYCLE,
CONF_WEB_SERVER_ID,
DEVICE_CLASS_APPARENT_POWER,
DEVICE_CLASS_AQI,
@ -72,8 +71,8 @@ from esphome.const import (
DEVICE_CLASS_WIND_SPEED,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity
from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@esphome/core"]
DEVICE_CLASSES = [

View file

@ -0,0 +1,161 @@
import logging
from esphome import automation
import esphome.codegen as cg
from esphome.components.http_request import CONF_HTTP_REQUEST_ID, HttpRequestComponent
from esphome.components.image import (
CONF_USE_TRANSPARENCY,
IMAGE_TYPE,
Image_,
validate_cross_dependencies,
)
import esphome.config_validation as cv
from esphome.const import (
CONF_BUFFER_SIZE,
CONF_FORMAT,
CONF_ID,
CONF_ON_ERROR,
CONF_RESIZE,
CONF_TRIGGER_ID,
CONF_TYPE,
CONF_URL,
)
AUTO_LOAD = ["image"]
DEPENDENCIES = ["display", "http_request"]
CODEOWNERS = ["@guillempages"]
MULTI_CONF = True
CONF_ON_DOWNLOAD_FINISHED = "on_download_finished"
_LOGGER = logging.getLogger(__name__)
online_image_ns = cg.esphome_ns.namespace("online_image")
ImageFormat = online_image_ns.enum("ImageFormat")
FORMAT_PNG = "PNG"
IMAGE_FORMAT = {FORMAT_PNG: ImageFormat.PNG} # Add new supported formats here
OnlineImage = online_image_ns.class_("OnlineImage", cg.PollingComponent, Image_)
# Actions
SetUrlAction = online_image_ns.class_(
"OnlineImageSetUrlAction", automation.Action, cg.Parented.template(OnlineImage)
)
ReleaseImageAction = online_image_ns.class_(
"OnlineImageReleaseAction", automation.Action, cg.Parented.template(OnlineImage)
)
# Triggers
DownloadFinishedTrigger = online_image_ns.class_(
"DownloadFinishedTrigger", automation.Trigger.template()
)
DownloadErrorTrigger = online_image_ns.class_(
"DownloadErrorTrigger", automation.Trigger.template()
)
ONLINE_IMAGE_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.declare_id(OnlineImage),
cv.GenerateID(CONF_HTTP_REQUEST_ID): cv.use_id(HttpRequestComponent),
#
# Common image options
#
cv.Optional(CONF_RESIZE): cv.dimensions,
cv.Optional(CONF_TYPE, default="BINARY"): cv.enum(IMAGE_TYPE, upper=True),
# Not setting default here on purpose; the default depends on the image type,
# and thus will be set in the "validate_cross_dependencies" validator.
cv.Optional(CONF_USE_TRANSPARENCY): cv.boolean,
#
# Online Image specific options
#
cv.Required(CONF_URL): cv.url,
cv.Required(CONF_FORMAT): cv.enum(IMAGE_FORMAT, upper=True),
cv.Optional(CONF_BUFFER_SIZE, default=2048): cv.int_range(256, 65536),
cv.Optional(CONF_ON_DOWNLOAD_FINISHED): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DownloadFinishedTrigger),
}
),
cv.Optional(CONF_ON_ERROR): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DownloadErrorTrigger),
}
),
}
).extend(cv.polling_component_schema("never"))
CONFIG_SCHEMA = cv.Schema(
cv.All(
ONLINE_IMAGE_SCHEMA,
validate_cross_dependencies,
cv.require_framework_version(
# esp8266 not supported yet; if enabled in the future, minimum version of 2.7.0 is needed
# esp8266_arduino=cv.Version(2, 7, 0),
esp32_arduino=cv.Version(0, 0, 0),
esp_idf=cv.Version(4, 0, 0),
),
)
)
SET_URL_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.use_id(OnlineImage),
cv.Required(CONF_URL): cv.templatable(cv.url),
}
)
RELEASE_IMAGE_SCHEMA = automation.maybe_simple_id(
{
cv.GenerateID(): cv.use_id(OnlineImage),
}
)
@automation.register_action("online_image.set_url", SetUrlAction, SET_URL_SCHEMA)
@automation.register_action(
"online_image.release", ReleaseImageAction, RELEASE_IMAGE_SCHEMA
)
async def online_image_action_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
if CONF_URL in config:
template_ = await cg.templatable(config[CONF_URL], args, cg.const_char_ptr)
cg.add(var.set_url(template_))
return var
async def to_code(config):
format = config[CONF_FORMAT]
if format in [FORMAT_PNG]:
cg.add_define("USE_ONLINE_IMAGE_PNG_SUPPORT")
cg.add_library("pngle", "1.0.2")
url = config[CONF_URL]
width, height = config.get(CONF_RESIZE, (0, 0))
transparent = config[CONF_USE_TRANSPARENCY]
var = cg.new_Pvariable(
config[CONF_ID],
url,
width,
height,
format,
config[CONF_TYPE],
config[CONF_BUFFER_SIZE],
)
await cg.register_component(var, config)
await cg.register_parented(var, config[CONF_HTTP_REQUEST_ID])
cg.add(var.set_transparency(transparent))
for conf in config.get(CONF_ON_DOWNLOAD_FINISHED, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_ERROR, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)

View file

@ -0,0 +1,44 @@
#include "image_decoder.h"
#include "online_image.h"
#include "esphome/core/log.h"
namespace esphome {
namespace online_image {
static const char *const TAG = "online_image.decoder";
void ImageDecoder::set_size(int width, int height) {
this->image_->resize_(width, height);
this->x_scale_ = static_cast<double>(this->image_->buffer_width_) / width;
this->y_scale_ = static_cast<double>(this->image_->buffer_height_) / height;
}
void ImageDecoder::draw(int x, int y, int w, int h, const Color &color) {
auto width = std::min(this->image_->buffer_width_, static_cast<int>(std::ceil((x + w) * this->x_scale_)));
auto height = std::min(this->image_->buffer_height_, static_cast<int>(std::ceil((y + h) * this->y_scale_)));
for (int i = x * this->x_scale_; i < width; i++) {
for (int j = y * this->y_scale_; j < height; j++) {
this->image_->draw_pixel_(i, j, color);
}
}
}
uint8_t *DownloadBuffer::data(size_t offset) {
if (offset > this->size_) {
ESP_LOGE(TAG, "Tried to access beyond download buffer bounds!!!");
return this->buffer_;
}
return this->buffer_ + offset;
}
size_t DownloadBuffer::read(size_t len) {
this->unread_ -= len;
if (this->unread_ > 0) {
memmove(this->data(), this->data(len), this->unread_);
}
return this->unread_;
}
} // namespace online_image
} // namespace esphome

View file

@ -0,0 +1,112 @@
#pragma once
#include "esphome/core/defines.h"
#include "esphome/core/color.h"
namespace esphome {
namespace online_image {
class OnlineImage;
/**
* @brief Class to abstract decoding different image formats.
*/
class ImageDecoder {
public:
/**
* @brief Construct a new Image Decoder object
*
* @param image The image to decode the stream into.
*/
ImageDecoder(OnlineImage *image) : image_(image) {}
virtual ~ImageDecoder() = default;
/**
* @brief Initialize the decoder.
*
* @param download_size The total number of bytes that need to be download for the image.
*/
virtual void prepare(uint32_t download_size) { this->download_size_ = download_size; }
/**
* @brief Decode a part of the image. It will try reading from the buffer.
* There is no guarantee that the whole available buffer will be read/decoded;
* the method will return the amount of bytes actually decoded, so that the
* unread content can be moved to the beginning.
*
* @param buffer The buffer to read from.
* @param size The maximum amount of bytes that can be read from the buffer.
* @return int The amount of bytes read. It can be 0 if the buffer does not have enough content to meaningfully
* decode anything, or negative in case of a decoding error.
*/
virtual int decode(uint8_t *buffer, size_t size);
/**
* @brief Request the image to be resized once the actual dimensions are known.
* Called by the callback functions, to be able to access the parent Image class.
*
* @param width The image's width.
* @param height The image's height.
*/
void set_size(int width, int height);
/**
* @brief Draw a rectangle on the display_buffer using the defined color.
* Will check the given coordinates for out-of-bounds, and clip the rectangle accordingly.
* In case of binary displays, the color will be converted to binary as well.
* Called by the callback functions, to be able to access the parent Image class.
*
* @param x The left-most coordinate of the rectangle.
* @param y The top-most coordinate of the rectangle.
* @param w The width of the rectangle.
* @param h The height of the rectangle.
* @param color The color to draw the rectangle with.
*/
void draw(int x, int y, int w, int h, const Color &color);
bool is_finished() const { return this->decoded_bytes_ == this->download_size_; }
protected:
OnlineImage *image_;
// Initializing to 1, to ensure it is different than initial "decoded_bytes_".
// Will be overwritten anyway once the download size is known.
uint32_t download_size_ = 1;
uint32_t decoded_bytes_ = 0;
double x_scale_ = 1.0;
double y_scale_ = 1.0;
};
class DownloadBuffer {
public:
DownloadBuffer(size_t size) : size_(size) {
this->buffer_ = this->allocator_.allocate(size);
this->reset();
}
virtual ~DownloadBuffer() { this->allocator_.deallocate(this->buffer_, this->size_); }
uint8_t *data(size_t offset = 0);
uint8_t *append() { return this->data(this->unread_); }
size_t unread() const { return this->unread_; }
size_t size() const { return this->size_; }
size_t free_capacity() const { return this->size_ - this->unread_; }
size_t read(size_t len);
size_t write(size_t len) {
this->unread_ += len;
return this->unread_;
}
void reset() { this->unread_ = 0; }
protected:
ExternalRAMAllocator<uint8_t> allocator_;
uint8_t *buffer_;
size_t size_;
/** Total number of downloaded bytes not yet read. */
size_t unread_;
};
} // namespace online_image
} // namespace esphome

View file

@ -0,0 +1,275 @@
#include "online_image.h"
#include "esphome/core/log.h"
static const char *const TAG = "online_image";
#include "image_decoder.h"
#ifdef USE_ONLINE_IMAGE_PNG_SUPPORT
#include "png_image.h"
#endif
namespace esphome {
namespace online_image {
using image::ImageType;
inline bool is_color_on(const Color &color) {
// This produces the most accurate monochrome conversion, but is slightly slower.
// return (0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b) > 127;
// Approximation using fast integer computations; produces acceptable results
// Equivalent to 0.25 * R + 0.5 * G + 0.25 * B
return ((color.r >> 2) + (color.g >> 1) + (color.b >> 2)) & 0x80;
}
OnlineImage::OnlineImage(const std::string &url, int width, int height, ImageFormat format, ImageType type,
uint32_t download_buffer_size)
: Image(nullptr, 0, 0, type),
buffer_(nullptr),
download_buffer_(download_buffer_size),
format_(format),
fixed_width_(width),
fixed_height_(height) {
this->set_url(url);
}
void OnlineImage::release() {
if (this->buffer_) {
ESP_LOGD(TAG, "Deallocating old buffer...");
this->allocator_.deallocate(this->buffer_, this->get_buffer_size_());
this->data_start_ = nullptr;
this->buffer_ = nullptr;
this->width_ = 0;
this->height_ = 0;
this->buffer_width_ = 0;
this->buffer_height_ = 0;
this->end_connection_();
}
}
bool OnlineImage::resize_(int width_in, int height_in) {
int width = this->fixed_width_;
int height = this->fixed_height_;
if (this->auto_resize_()) {
width = width_in;
height = height_in;
if (this->width_ != width && this->height_ != height) {
this->release();
}
}
if (this->buffer_) {
return false;
}
auto new_size = this->get_buffer_size_(width, height);
ESP_LOGD(TAG, "Allocating new buffer of %d Bytes...", new_size);
delay_microseconds_safe(2000);
this->buffer_ = this->allocator_.allocate(new_size);
if (this->buffer_) {
this->buffer_width_ = width;
this->buffer_height_ = height;
this->width_ = width;
ESP_LOGD(TAG, "New size: (%d, %d)", width, height);
} else {
#if defined(USE_ESP8266)
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
int max_block = ESP.getMaxFreeBlockSize();
#elif defined(USE_ESP32)
int max_block = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL);
#else
int max_block = -1;
#endif
ESP_LOGE(TAG, "allocation failed. Biggest block in heap: %d Bytes", max_block);
this->end_connection_();
return false;
}
return true;
}
void OnlineImage::update() {
if (this->decoder_) {
ESP_LOGW(TAG, "Image already being updated.");
return;
} else {
ESP_LOGI(TAG, "Updating image");
}
this->downloader_ = this->parent_->get(this->url_);
if (this->downloader_ == nullptr) {
ESP_LOGE(TAG, "Download failed.");
this->end_connection_();
this->download_error_callback_.call();
return;
}
int http_code = this->downloader_->status_code;
if (http_code == HTTP_CODE_NOT_MODIFIED) {
// Image hasn't changed on server. Skip download.
this->end_connection_();
return;
}
if (http_code != HTTP_CODE_OK) {
ESP_LOGE(TAG, "HTTP result: %d", http_code);
this->end_connection_();
this->download_error_callback_.call();
return;
}
ESP_LOGD(TAG, "Starting download");
size_t total_size = this->downloader_->content_length;
#ifdef USE_ONLINE_IMAGE_PNG_SUPPORT
if (this->format_ == ImageFormat::PNG) {
this->decoder_ = esphome::make_unique<PngDecoder>(this);
}
#endif // ONLINE_IMAGE_PNG_SUPPORT
if (!this->decoder_) {
ESP_LOGE(TAG, "Could not instantiate decoder. Image format unsupported.");
this->end_connection_();
this->download_error_callback_.call();
return;
}
this->decoder_->prepare(total_size);
ESP_LOGI(TAG, "Downloading image");
}
void OnlineImage::loop() {
if (!this->decoder_) {
// Not decoding at the moment => nothing to do.
return;
}
if (!this->downloader_ || this->decoder_->is_finished()) {
ESP_LOGD(TAG, "Image fully downloaded");
this->data_start_ = buffer_;
this->width_ = buffer_width_;
this->height_ = buffer_height_;
this->end_connection_();
this->download_finished_callback_.call();
return;
}
if (this->downloader_ == nullptr) {
ESP_LOGE(TAG, "Downloader not instantiated; cannot download");
return;
}
size_t available = this->download_buffer_.free_capacity();
if (available) {
auto len = this->downloader_->read(this->download_buffer_.append(), available);
if (len > 0) {
this->download_buffer_.write(len);
auto fed = this->decoder_->decode(this->download_buffer_.data(), this->download_buffer_.unread());
if (fed < 0) {
ESP_LOGE(TAG, "Error when decoding image.");
this->end_connection_();
this->download_error_callback_.call();
return;
}
this->download_buffer_.read(fed);
}
}
}
void OnlineImage::draw_pixel_(int x, int y, Color color) {
if (!this->buffer_) {
ESP_LOGE(TAG, "Buffer not allocated!");
return;
}
if (x < 0 || y < 0 || x >= this->buffer_width_ || y >= this->buffer_height_) {
ESP_LOGE(TAG, "Tried to paint a pixel (%d,%d) outside the image!", x, y);
return;
}
uint32_t pos = this->get_position_(x, y);
switch (this->type_) {
case ImageType::IMAGE_TYPE_BINARY: {
const uint32_t width_8 = ((this->width_ + 7u) / 8u) * 8u;
const uint32_t pos = x + y * width_8;
if ((this->has_transparency() && color.w > 127) || is_color_on(color)) {
this->buffer_[pos / 8u] |= (0x80 >> (pos % 8u));
} else {
this->buffer_[pos / 8u] &= ~(0x80 >> (pos % 8u));
}
break;
}
case ImageType::IMAGE_TYPE_GRAYSCALE: {
uint8_t gray = static_cast<uint8_t>(0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b);
if (this->has_transparency()) {
if (gray == 1) {
gray = 0;
}
if (color.w < 0x80) {
gray = 1;
}
}
this->buffer_[pos] = gray;
break;
}
case ImageType::IMAGE_TYPE_RGB565: {
uint16_t col565 = display::ColorUtil::color_to_565(color);
if (this->has_transparency()) {
if (col565 == 0x0020) {
col565 = 0;
}
if (color.w < 0x80) {
col565 = 0x0020;
}
}
this->buffer_[pos + 0] = static_cast<uint8_t>((col565 >> 8) & 0xFF);
this->buffer_[pos + 1] = static_cast<uint8_t>(col565 & 0xFF);
break;
}
case ImageType::IMAGE_TYPE_RGBA: {
this->buffer_[pos + 0] = color.r;
this->buffer_[pos + 1] = color.g;
this->buffer_[pos + 2] = color.b;
this->buffer_[pos + 3] = color.w;
break;
}
case ImageType::IMAGE_TYPE_RGB24:
default: {
if (this->has_transparency()) {
if (color.b == 1 && color.r == 0 && color.g == 0) {
color.b = 0;
}
if (color.w < 0x80) {
color.r = 0;
color.g = 0;
color.b = 1;
}
}
this->buffer_[pos + 0] = color.r;
this->buffer_[pos + 1] = color.g;
this->buffer_[pos + 2] = color.b;
break;
}
}
}
void OnlineImage::end_connection_() {
if (this->downloader_) {
this->downloader_->end();
this->downloader_ = nullptr;
}
this->decoder_.reset();
this->download_buffer_.reset();
}
bool OnlineImage::validate_url_(const std::string &url) {
if ((url.length() < 8) || (url.find("http") != 0) || (url.find("://") == std::string::npos)) {
ESP_LOGE(TAG, "URL is invalid and/or must be prefixed with 'http://' or 'https://'");
return false;
}
return true;
}
void OnlineImage::add_on_finished_callback(std::function<void()> &&callback) {
this->download_finished_callback_.add(std::move(callback));
}
void OnlineImage::add_on_error_callback(std::function<void()> &&callback) {
this->download_error_callback_.add(std::move(callback));
}
} // namespace online_image
} // namespace esphome

View file

@ -0,0 +1,184 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/helpers.h"
#include "esphome/components/http_request/http_request.h"
#include "esphome/components/image/image.h"
#include "image_decoder.h"
namespace esphome {
namespace online_image {
using t_http_codes = enum {
HTTP_CODE_OK = 200,
HTTP_CODE_NOT_MODIFIED = 304,
HTTP_CODE_NOT_FOUND = 404,
};
/**
* @brief Format that the image is encoded with.
*/
enum ImageFormat {
/** Automatically detect from MIME type. Not supported yet. */
AUTO,
/** JPEG format. Not supported yet. */
JPEG,
/** PNG format. */
PNG,
};
/**
* @brief Download an image from a given URL, and decode it using the specified decoder.
* The image will then be stored in a buffer, so that it can be re-displayed without the
* need to re-download or re-decode.
*/
class OnlineImage : public PollingComponent,
public image::Image,
public Parented<esphome::http_request::HttpRequestComponent> {
public:
/**
* @brief Construct a new OnlineImage object.
*
* @param url URL to download the image from.
* @param width Desired width of the target image area.
* @param height Desired height of the target image area.
* @param format Format that the image is encoded in (@see ImageFormat).
* @param buffer_size Size of the buffer used to download the image.
*/
OnlineImage(const std::string &url, int width, int height, ImageFormat format, image::ImageType type,
uint32_t buffer_size);
void update() override;
void loop() override;
/** Set the URL to download the image from. */
void set_url(const std::string &url) {
if (this->validate_url_(url)) {
this->url_ = url;
}
}
/**
* Release the buffer storing the image. The image will need to be downloaded again
* to be able to be displayed.
*/
void release();
void add_on_finished_callback(std::function<void()> &&callback);
void add_on_error_callback(std::function<void()> &&callback);
protected:
bool validate_url_(const std::string &url);
using Allocator = ExternalRAMAllocator<uint8_t>;
Allocator allocator_{Allocator::Flags::ALLOW_FAILURE};
uint32_t get_buffer_size_() const { return get_buffer_size_(this->buffer_width_, this->buffer_height_); }
int get_buffer_size_(int width, int height) const {
return std::ceil(image::image_type_to_bpp(this->type_) * width * height / 8.0);
}
int get_position_(int x, int y) const {
return ((x + y * this->buffer_width_) * image::image_type_to_bpp(this->type_)) / 8;
}
ESPHOME_ALWAYS_INLINE bool auto_resize_() const { return this->fixed_width_ == 0 || this->fixed_height_ == 0; }
bool resize_(int width, int height);
/**
* @brief Draw a pixel into the buffer.
*
* This is used by the decoder to fill the buffer that will later be displayed
* by the `draw` method. This will internally convert the supplied 32 bit RGBA
* color into the requested image storage format.
*
* @param x Horizontal pixel position.
* @param y Vertical pixel position.
* @param color 32 bit color to put into the pixel.
*/
void draw_pixel_(int x, int y, Color color);
void end_connection_();
CallbackManager<void()> download_finished_callback_{};
CallbackManager<void()> download_error_callback_{};
std::shared_ptr<http_request::HttpContainer> downloader_{nullptr};
std::unique_ptr<ImageDecoder> decoder_{nullptr};
uint8_t *buffer_;
DownloadBuffer download_buffer_;
const ImageFormat format_;
std::string url_{""};
/** width requested on configuration, or 0 if non specified. */
const int fixed_width_;
/** height requested on configuration, or 0 if non specified. */
const int fixed_height_;
/**
* Actual width of the current image. If fixed_width_ is specified,
* this will be equal to it; otherwise it will be set once the decoding
* starts and the original size is known.
* This needs to be separate from "BaseImage::get_width()" because the latter
* must return 0 until the image has been decoded (to avoid showing partially
* decoded images).
*/
int buffer_width_;
/**
* Actual height of the current image. If fixed_height_ is specified,
* this will be equal to it; otherwise it will be set once the decoding
* starts and the original size is known.
* This needs to be separate from "BaseImage::get_height()" because the latter
* must return 0 until the image has been decoded (to avoid showing partially
* decoded images).
*/
int buffer_height_;
friend void ImageDecoder::set_size(int width, int height);
friend void ImageDecoder::draw(int x, int y, int w, int h, const Color &color);
};
template<typename... Ts> class OnlineImageSetUrlAction : public Action<Ts...> {
public:
OnlineImageSetUrlAction(OnlineImage *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(const char *, url)
void play(Ts... x) override {
this->parent_->set_url(this->url_.value(x...));
this->parent_->update();
}
protected:
OnlineImage *parent_;
};
template<typename... Ts> class OnlineImageReleaseAction : public Action<Ts...> {
public:
OnlineImageReleaseAction(OnlineImage *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(const char *, url)
void play(Ts... x) override { this->parent_->release(); }
protected:
OnlineImage *parent_;
};
class DownloadFinishedTrigger : public Trigger<> {
public:
explicit DownloadFinishedTrigger(OnlineImage *parent) {
parent->add_on_finished_callback([this]() { this->trigger(); });
}
};
class DownloadErrorTrigger : public Trigger<> {
public:
explicit DownloadErrorTrigger(OnlineImage *parent) {
parent->add_on_error_callback([this]() { this->trigger(); });
}
};
} // namespace online_image
} // namespace esphome

View file

@ -0,0 +1,68 @@
#include "png_image.h"
#ifdef USE_ONLINE_IMAGE_PNG_SUPPORT
#include "esphome/components/display/display_buffer.h"
#include "esphome/core/application.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
static const char *const TAG = "online_image.png";
namespace esphome {
namespace online_image {
/**
* @brief Callback method that will be called by the PNGLE engine when the basic
* data of the image is received (i.e. width and height);
*
* @param pngle The PNGLE object, including the context data.
* @param w The width of the image.
* @param h The height of the image.
*/
static void init_callback(pngle_t *pngle, uint32_t w, uint32_t h) {
PngDecoder *decoder = (PngDecoder *) pngle_get_user_data(pngle);
decoder->set_size(w, h);
}
/**
* @brief Callback method that will be called by the PNGLE engine when a chunk
* of the image is decoded.
*
* @param pngle The PNGLE object, including the context data.
* @param x The X coordinate to draw the rectangle on.
* @param y The Y coordinate to draw the rectangle on.
* @param w The width of the rectangle to draw.
* @param h The height of the rectangle to draw.
* @param rgba The color to paint the rectangle in.
*/
static void draw_callback(pngle_t *pngle, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint8_t rgba[4]) {
PngDecoder *decoder = (PngDecoder *) pngle_get_user_data(pngle);
Color color(rgba[0], rgba[1], rgba[2], rgba[3]);
decoder->draw(x, y, w, h, color);
}
void PngDecoder::prepare(uint32_t download_size) {
ImageDecoder::prepare(download_size);
pngle_set_user_data(this->pngle_, this);
pngle_set_init_callback(this->pngle_, init_callback);
pngle_set_draw_callback(this->pngle_, draw_callback);
}
int HOT PngDecoder::decode(uint8_t *buffer, size_t size) {
if (size < 256 && size < this->download_size_ - this->decoded_bytes_) {
ESP_LOGD(TAG, "Waiting for data");
return 0;
}
auto fed = pngle_feed(this->pngle_, buffer, size);
if (fed < 0) {
ESP_LOGE(TAG, "Error decoding image: %s", pngle_error(this->pngle_));
} else {
this->decoded_bytes_ += fed;
}
return fed;
}
} // namespace online_image
} // namespace esphome
#endif // USE_ONLINE_IMAGE_PNG_SUPPORT

View file

@ -0,0 +1,33 @@
#pragma once
#include "image_decoder.h"
#ifdef USE_ONLINE_IMAGE_PNG_SUPPORT
#include <pngle.h>
namespace esphome {
namespace online_image {
/**
* @brief Image decoder specialization for PNG images.
*/
class PngDecoder : public ImageDecoder {
public:
/**
* @brief Construct a new PNG Decoder object.
*
* @param display The image to decode the stream into.
*/
PngDecoder(OnlineImage *image) : ImageDecoder(image), pngle_(pngle_new()) {}
~PngDecoder() override { pngle_destroy(this->pngle_); }
void prepare(uint32_t download_size) override;
int HOT decode(uint8_t *buffer, size_t size) override;
protected:
pngle_t *pngle_;
};
} // namespace online_image
} // namespace esphome
#endif // USE_ONLINE_IMAGE_PNG_SUPPORT

View file

@ -49,7 +49,7 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase,
#ifdef USE_ESP32
void configure_rmt_();
uint32_t current_carrier_frequency_{UINT32_MAX};
uint32_t current_carrier_frequency_{38000};
bool initialized_{false};
std::vector<rmt_item32_t> rmt_temp_;
esp_err_t error_code_{ESP_OK};

View file

@ -1,20 +1,20 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
import esphome.codegen as cg
from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import (
CONF_CYCLE,
CONF_ENTITY_CATEGORY,
CONF_ICON,
CONF_ID,
CONF_INDEX,
CONF_MODE,
CONF_MQTT_ID,
CONF_ON_VALUE,
CONF_OPERATION,
CONF_OPTION,
CONF_TRIGGER_ID,
CONF_MQTT_ID,
CONF_WEB_SERVER_ID,
CONF_CYCLE,
CONF_MODE,
CONF_OPERATION,
CONF_INDEX,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_generator import MockObjClass

View file

@ -1,22 +1,27 @@
import math
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
import esphome.codegen as cg
from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import (
CONF_DEVICE_CLASS,
CONF_ABOVE,
CONF_ACCURACY_DECIMALS,
CONF_ALPHA,
CONF_BELOW,
CONF_DEVICE_CLASS,
CONF_ENTITY_CATEGORY,
CONF_EXPIRE_AFTER,
CONF_FILTERS,
CONF_FORCE_UPDATE,
CONF_FROM,
CONF_ICON,
CONF_ID,
CONF_IGNORE_OUT_OF_RANGE,
CONF_MAX_VALUE,
CONF_METHOD,
CONF_MIN_VALUE,
CONF_MQTT_ID,
CONF_MULTIPLE,
CONF_ON_RAW_VALUE,
CONF_ON_VALUE,
@ -30,14 +35,9 @@ from esphome.const import (
CONF_TRIGGER_ID,
CONF_TYPE,
CONF_UNIT_OF_MEASUREMENT,
CONF_WINDOW_SIZE,
CONF_MQTT_ID,
CONF_WEB_SERVER_ID,
CONF_FORCE_UPDATE,
CONF_VALUE,
CONF_MIN_VALUE,
CONF_MAX_VALUE,
CONF_METHOD,
CONF_WEB_SERVER_ID,
CONF_WINDOW_SIZE,
DEVICE_CLASS_APPARENT_POWER,
DEVICE_CLASS_AQI,
DEVICE_CLASS_ATMOSPHERIC_PRESSURE,

View file

@ -7,10 +7,6 @@ namespace spi {
const char *const TAG = "spi";
SPIDelegate *const SPIDelegate::NULL_DELEGATE = // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
new SPIDelegateDummy();
// https://bugs.llvm.org/show_bug.cgi?id=48040
bool SPIDelegate::is_ready() { return true; }
GPIOPin *const NullPin::NULL_PIN = new NullPin(); // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
@ -79,8 +75,6 @@ void SPIComponent::dump_config() {
}
}
void SPIDelegateDummy::begin_transaction() { ESP_LOGE(TAG, "SPIDevice not initialised - did you call spi_setup()?"); }
uint8_t SPIDelegateBitBash::transfer(uint8_t data) { return this->transfer_(data, 8); }
void SPIDelegateBitBash::write(uint16_t data, size_t num_bits) { this->transfer_(data, num_bits); }

View file

@ -163,8 +163,6 @@ class Utility {
}
};
class SPIDelegateDummy;
// represents a device attached to an SPI bus, with a defined clock rate, mode and bit order. On Arduino this is
// a thin wrapper over SPIClass.
class SPIDelegate {
@ -250,21 +248,6 @@ class SPIDelegate {
uint32_t data_rate_{1000000};
SPIMode mode_{MODE0};
GPIOPin *cs_pin_{NullPin::NULL_PIN};
static SPIDelegate *const NULL_DELEGATE; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
};
/**
* A dummy SPIDelegate that complains if it's used.
*/
class SPIDelegateDummy : public SPIDelegate {
public:
SPIDelegateDummy() = default;
uint8_t transfer(uint8_t data) override { return 0; }
void end_transaction() override{};
void begin_transaction() override;
};
/**
@ -382,7 +365,7 @@ class SPIClient {
virtual void spi_teardown() {
this->parent_->unregister_device(this);
this->delegate_ = SPIDelegate::NULL_DELEGATE;
this->delegate_ = nullptr;
}
bool spi_is_ready() { return this->delegate_->is_ready(); }
@ -393,7 +376,7 @@ class SPIClient {
uint32_t data_rate_{1000000};
SPIComponent *parent_{nullptr};
GPIOPin *cs_{nullptr};
SPIDelegate *delegate_{SPIDelegate::NULL_DELEGATE};
SPIDelegate *delegate_{nullptr};
};
/**

View file

@ -647,7 +647,7 @@ void Sprinkler::set_valve_run_duration(const optional<size_t> valve_number, cons
return;
}
auto call = this->valve_[valve_number.value()].run_duration_number->make_call();
if (this->valve_[valve_number.value()].run_duration_number->traits.get_unit_of_measurement() == min_str) {
if (this->valve_[valve_number.value()].run_duration_number->traits.get_unit_of_measurement() == MIN_STR) {
call.set_value(run_duration.value() / 60.0);
} else {
call.set_value(run_duration.value());
@ -729,7 +729,7 @@ uint32_t Sprinkler::valve_run_duration(const size_t valve_number) {
return 0;
}
if (this->valve_[valve_number].run_duration_number != nullptr) {
if (this->valve_[valve_number].run_duration_number->traits.get_unit_of_measurement() == min_str) {
if (this->valve_[valve_number].run_duration_number->traits.get_unit_of_measurement() == MIN_STR) {
return static_cast<uint32_t>(roundf(this->valve_[valve_number].run_duration_number->state * 60));
} else {
return static_cast<uint32_t>(roundf(this->valve_[valve_number].run_duration_number->state));

View file

@ -11,7 +11,7 @@
namespace esphome {
namespace sprinkler {
const std::string min_str = "min";
const std::string MIN_STR = "min";
enum SprinklerState : uint8_t {
// NOTE: these states are used by both SprinklerValveOperator and Sprinkler (the controller)!

View file

@ -1,8 +1,8 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.automation import Condition, maybe_simple_id
import esphome.codegen as cg
from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import (
CONF_DEVICE_CLASS,
CONF_ENTITY_CATEGORY,
@ -10,11 +10,11 @@ from esphome.const import (
CONF_ID,
CONF_INVERTED,
CONF_MQTT_ID,
CONF_WEB_SERVER_ID,
CONF_ON_TURN_OFF,
CONF_ON_TURN_ON,
CONF_RESTORE_MODE,
CONF_TRIGGER_ID,
CONF_WEB_SERVER_ID,
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_OUTLET,
DEVICE_CLASS_SWITCH,

View file

@ -1,18 +1,18 @@
from typing import Optional
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
import esphome.codegen as cg
from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import (
CONF_ID,
CONF_MODE,
CONF_MQTT_ID,
CONF_ON_VALUE,
CONF_TRIGGER_ID,
CONF_MQTT_ID,
CONF_WEB_SERVER_ID,
CONF_VALUE,
CONF_WEB_SERVER_ID,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity

View file

@ -1,21 +1,21 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
import esphome.codegen as cg
from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import (
CONF_DEVICE_CLASS,
CONF_ENTITY_CATEGORY,
CONF_FILTERS,
CONF_FROM,
CONF_ICON,
CONF_ID,
CONF_ON_VALUE,
CONF_ON_RAW_VALUE,
CONF_TRIGGER_ID,
CONF_MQTT_ID,
CONF_WEB_SERVER_ID,
CONF_ON_RAW_VALUE,
CONF_ON_VALUE,
CONF_STATE,
CONF_FROM,
CONF_TO,
CONF_TRIGGER_ID,
CONF_WEB_SERVER_ID,
DEVICE_CLASS_DATE,
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_TIMESTAMP,

View file

@ -1,8 +1,8 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.automation import maybe_simple_id, Condition
from esphome.automation import Condition, maybe_simple_id
import esphome.codegen as cg
from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import (
CONF_DEVICE_CLASS,
CONF_ID,

View file

@ -91,7 +91,7 @@ _LOGGER = logging.getLogger(__name__)
# pylint: disable=consider-using-f-string
VARIABLE_PROG = re.compile(
"\\$([{0}]+|\\{{[{0}]*\\}})".format(VALID_SUBSTITUTIONS_CHARACTERS)
f"\\$([{VALID_SUBSTITUTIONS_CHARACTERS}]+|\\{{[{VALID_SUBSTITUTIONS_CHARACTERS}]*\\}})"
)
# pylint: disable=invalid-name

View file

@ -308,6 +308,7 @@ CONF_FLASH_LENGTH = "flash_length"
CONF_FLASH_TRANSITION_LENGTH = "flash_transition_length"
CONF_FLOW = "flow"
CONF_FLOW_CONTROL_PIN = "flow_control_pin"
CONF_FONT = "font"
CONF_FOR = "for"
CONF_FORCE_UPDATE = "force_update"
CONF_FOREGROUND_COLOR = "foreground_color"
@ -407,6 +408,7 @@ CONF_INVERTED = "inverted"
CONF_IP_ADDRESS = "ip_address"
CONF_IRQ_PIN = "irq_pin"
CONF_IS_RGBW = "is_rgbw"
CONF_ITEMS = "items"
CONF_JS_INCLUDE = "js_include"
CONF_JS_URL = "js_url"
CONF_JVC = "jvc"
@ -841,6 +843,7 @@ CONF_TEMPERATURE = "temperature"
CONF_TEMPERATURE_OFFSET = "temperature_offset"
CONF_TEMPERATURE_SOURCE = "temperature_source"
CONF_TEMPERATURE_STEP = "temperature_step"
CONF_TEXT = "text"
CONF_TEXT_SENSORS = "text_sensors"
CONF_THEN = "then"
CONF_THRESHOLD = "threshold"

View file

@ -336,7 +336,7 @@ class ID:
else:
self.is_manual = is_manual
self.is_declaration = is_declaration
self.type: Optional["MockObjClass"] = type
self.type: Optional[MockObjClass] = type
def resolve(self, registered_ids):
from esphome.config_validation import RESERVED_IDS
@ -500,7 +500,7 @@ class EsphomeCore:
# The relative path to where all build files are stored
self.build_path: Optional[str] = None
# The validated configuration, this is None until the config has been validated
self.config: Optional["ConfigType"] = None
self.config: Optional[ConfigType] = None
# The pending tasks in the task queue (mostly for C++ generation)
# This is a priority queue (with heapq)
# Each item is a tuple of form: (-priority, unique number, task)
@ -508,17 +508,17 @@ class EsphomeCore:
# Task counter for pending tasks
self.task_counter = 0
# The variable cache, for each ID this holds a MockObj of the variable obj
self.variables: dict[str, "MockObj"] = {}
self.variables: dict[str, MockObj] = {}
# A list of statements that go in the main setup() block
self.main_statements: list["Statement"] = []
self.main_statements: list[Statement] = []
# A list of statements to insert in the global block (includes and global variables)
self.global_statements: list["Statement"] = []
self.global_statements: list[Statement] = []
# A set of platformio libraries to add to the project
self.libraries: list[Library] = []
# A set of build flags to set in the platformio project
self.build_flags: set[str] = set()
# A set of defines to set for the compile process in esphome/core/defines.h
self.defines: set["Define"] = set()
self.defines: set[Define] = set()
# A map of all platformio options to apply
self.platformio_options: dict[str, Union[str, list[str]]] = {}
# A set of strings of names of loaded integrations, used to find namespace ID conflicts

View file

@ -54,6 +54,7 @@
#define USE_MQTT
#define USE_NEXTION_TFT_UPLOAD
#define USE_NUMBER
#define USE_ONLINE_IMAGE_PNG_SUPPORT
#define USE_OTA
#define USE_OTA_PASSWORD
#define USE_OTA_STATE_CALLBACK

View file

@ -62,24 +62,6 @@ class GPIOPin {
virtual bool is_internal() { return false; }
};
/**
* A pin to replace those that don't exist.
*/
class NullPin : public GPIOPin {
public:
void setup() override {}
void pin_mode(gpio::Flags _) override {}
bool digital_read() override { return false; }
void digital_write(bool _) override {}
std::string dump_summary() const override { return {"Not used"}; }
};
static GPIOPin *const NULL_PIN = new NullPin();
/// Copy of GPIOPin that is safe to use from ISRs (with no virtual functions)
class ISRInternalGPIOPin {
public:

View file

@ -680,7 +680,7 @@ template<class T> class ExternalRAMAllocator {
}
private:
Flags flags_{Flags::NONE};
Flags flags_{Flags::ALLOW_FAILURE};
};
/// @}

View file

@ -3,7 +3,6 @@ import hashlib
import json
import logging
import ssl
import sys
import time
import paho.mqtt.client as mqtt
@ -103,10 +102,7 @@ def prepare(
if config[CONF_MQTT].get(CONF_SSL_FINGERPRINTS) or config[CONF_MQTT].get(
CONF_CERTIFICATE_AUTHORITY
):
if sys.version_info >= (2, 7, 13):
tls_version = ssl.PROTOCOL_TLS # pylint: disable=no-member
else:
tls_version = ssl.PROTOCOL_SSLv23
client.tls_set(
ca_certs=None,
certfile=None,

View file

@ -40,6 +40,7 @@ lib_deps =
wjtje/qr-code-generator-library@1.7.0 ; qr_code
functionpointer/arduino-MLX90393@1.0.0 ; mlx90393
pavlodn/HaierProtocol@0.9.31 ; haier
kikuchan98/pngle@1.0.2 ; online_image
; This is using the repository until a new release is published to PlatformIO
https://github.com/Sensirion/arduino-gas-index-algorithm.git#3.2.1 ; Sensirion Gas Index Algorithm Arduino Library
lvgl/lvgl@8.4.0 ; lvgl

View file

@ -1,13 +1,13 @@
#!/usr/bin/env python3
from pathlib import Path
import sys
import argparse
from collections import defaultdict
from pathlib import Path
import sys
from esphome.helpers import write_file_if_changed
from esphome.config import get_component, get_platform
from esphome.core import CORE
from esphome.const import KEY_CORE, KEY_TARGET_FRAMEWORK
from esphome.core import CORE
from esphome.helpers import write_file_if_changed
parser = argparse.ArgumentParser()
parser.add_argument(

View file

@ -1,9 +1,10 @@
import argparse
import glob
import inspect
import json
import argparse
import os
import glob
import re
import voluptuous as vol
# NOTE: Cannot import other esphome components globally as a modification in vol_schema
@ -94,13 +95,12 @@ load_components()
# Import esphome after loading components (so schema is tracked)
# pylint: disable=wrong-import-position
import esphome.core as esphome_core
import esphome.config_validation as cv
from esphome import automation
from esphome import pins
from esphome import automation, pins
from esphome.components import remote_base
from esphome.loader import get_platform, CORE_COMPONENTS_PATH
import esphome.config_validation as cv
import esphome.core as esphome_core
from esphome.helpers import write_file_if_changed
from esphome.loader import CORE_COMPONENTS_PATH, get_platform
from esphome.util import Registry
# pylint: enable=wrong-import-position

View file

@ -1,8 +1,8 @@
#!/usr/bin/env python3
import argparse
import re
from dataclasses import dataclass
import re
import sys

View file

@ -1,15 +1,6 @@
#!/usr/bin/env python3
from helpers import (
print_error_for_file,
get_output,
git_ls_files,
filter_changed,
get_binary,
)
import argparse
import click
import colorama
import multiprocessing
import os
import queue
@ -18,6 +9,9 @@ import subprocess
import sys
import threading
import click
import colorama
from helpers import filter_changed, get_binary, git_ls_files, print_error_for_file
def run_format(executable, args, queue, lock, failed_files):

View file

@ -1,21 +1,6 @@
#!/usr/bin/env python3
from helpers import (
print_error_for_file,
get_output,
filter_grep,
build_all_include,
temp_header_file,
git_ls_files,
filter_changed,
load_idedata,
root_path,
basepath,
get_binary,
)
import argparse
import click
import colorama
import multiprocessing
import os
import queue
@ -26,6 +11,20 @@ import sys
import tempfile
import threading
import click
import colorama
from helpers import (
basepath,
build_all_include,
filter_changed,
filter_grep,
get_binary,
git_ls_files,
load_idedata,
print_error_for_file,
root_path,
temp_header_file,
)
def clang_options(idedata):

View file

@ -1,8 +1,8 @@
import json
import os.path
from pathlib import Path
import re
import subprocess
from pathlib import Path
import colorama

View file

@ -1,19 +1,20 @@
#!/usr/bin/env python3
from helpers import (
styled,
print_error_for_file,
get_output,
get_err,
git_ls_files,
filter_changed,
)
import argparse
import colorama
import os
import re
import sys
import colorama
from helpers import (
filter_changed,
get_err,
get_output,
git_ls_files,
print_error_for_file,
styled,
)
curfile = None

View file

@ -1,11 +1,10 @@
#!/usr/bin/env python3
import argparse
from pathlib import Path
import sys
import argparse
from helpers import git_ls_files, changed_files
from esphome.loader import get_component, get_platform
from esphome.core import CORE
from helpers import changed_files, git_ls_files
from esphome.const import (
KEY_CORE,
KEY_TARGET_FRAMEWORK,
@ -13,6 +12,8 @@ from esphome.const import (
PLATFORM_ESP32,
PLATFORM_ESP8266,
)
from esphome.core import CORE
from esphome.loader import get_component, get_platform
def filter_component_files(str):

View file

@ -24,6 +24,33 @@ display:
invert_colors: false
update_interval: never
binary_sensor:
- platform: gpio
internal: true
id: up_button
pin:
number: GPIO38
inverted: true
- platform: gpio
internal: true
id: down_button
pin:
number: GPIO37
inverted: true
- platform: gpio
internal: true
id: select_button
pin:
number: GPIO39
inverted: true
lvgl:
encoders:
group: switches
enter_button: select_button
sensor:
left_button: up_button
right_button: down_button
packages:
lvgl: !include lvgl-package.yaml

View file

@ -67,7 +67,7 @@ lvgl:
displays:
- tft_display
- second_display
rotary_encoders:
encoders:
sensor: encoder
enter_button: pushbutton
group: general

View file

@ -0,0 +1,18 @@
<<: !include common.yaml
spi:
- id: spi_main_lcd
clk_pin: 16
mosi_pin: 17
miso_pin: 15
display:
- platform: ili9xxx
id: main_lcd
model: ili9342
cs_pin: 12
dc_pin: 13
reset_pin: 21
lambda: |-
it.fill(Color(0, 0, 0));
it.image(0, 0, id(online_rgba_image));

View file

@ -0,0 +1,18 @@
<<: !include common.yaml
spi:
- id: spi_main_lcd
clk_pin: 14
mosi_pin: 13
miso_pin: 12
display:
- platform: ili9xxx
id: main_lcd
model: ili9342
cs_pin: 15
dc_pin: 3
reset_pin: 1
lambda: |-
it.fill(Color(0, 0, 0));
it.image(0, 0, id(online_rgba_image));

View file

@ -0,0 +1,37 @@
wifi:
ssid: MySSID
password: password1
# Purposely test that `online_image:` does auto-load `image:`
# Keep the `image:` undefined.
# image:
online_image:
- id: online_binary_image
url: http://www.libpng.org/pub/png/img_png/pnglogo-blk-tiny.png
format: PNG
type: BINARY
resize: 50x50
- id: online_binary_transparent_image
url: http://www.libpng.org/pub/png/img_png/pnglogo-blk-tiny.png
type: TRANSPARENT_BINARY
format: png
- id: online_rgba_image
url: http://www.libpng.org/pub/png/img_png/pnglogo-blk-tiny.png
format: PNG
type: RGBA
- id: online_rgb24_image
url: http://www.libpng.org/pub/png/img_png/pnglogo-blk-tiny.png
format: PNG
type: RGB24
use_transparency: true
# Check the set_url action
time:
- platform: sntp
on_time:
- at: "13:37:42"
then:
- online_image.set_url:
id: online_rgba_image
url: http://www.example.org/example.png

View file

@ -0,0 +1,4 @@
<<: !include common-esp32.yaml
http_request:
verify_ssl: false

View file

@ -0,0 +1,4 @@
<<: !include common-esp32.yaml
http_request: