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/noblex/* @AGalfra
esphome/components/number/* @esphome/core esphome/components/number/* @esphome/core
esphome/components/one_wire/* @ssieb esphome/components/one_wire/* @ssieb
esphome/components/online_image/* @guillempages
esphome/components/ota/* @esphome/core esphome/components/ota/* @esphome/core
esphome/components/output/* @esphome/core esphome/components/output/* @esphome/core
esphome/components/pca6416a/* @Mat931 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 import automation, core
from esphome.automation import Condition, maybe_simple_id from esphome.automation import Condition, maybe_simple_id
import esphome.codegen as cg
from esphome.components import mqtt, web_server from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_DELAY, CONF_DELAY,
CONF_DEVICE_CLASS, CONF_DEVICE_CLASS,
@ -16,6 +14,7 @@ from esphome.const import (
CONF_INVERTED, CONF_INVERTED,
CONF_MAX_LENGTH, CONF_MAX_LENGTH,
CONF_MIN_LENGTH, CONF_MIN_LENGTH,
CONF_MQTT_ID,
CONF_ON_CLICK, CONF_ON_CLICK,
CONF_ON_DOUBLE_CLICK, CONF_ON_DOUBLE_CLICK,
CONF_ON_MULTI_CLICK, CONF_ON_MULTI_CLICK,
@ -26,7 +25,6 @@ from esphome.const import (
CONF_STATE, CONF_STATE,
CONF_TIMING, CONF_TIMING,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_MQTT_ID,
CONF_WEB_SERVER_ID, CONF_WEB_SERVER_ID,
DEVICE_CLASS_BATTERY, DEVICE_CLASS_BATTERY,
DEVICE_CLASS_BATTERY_CHARGING, DEVICE_CLASS_BATTERY_CHARGING,
@ -59,6 +57,8 @@ from esphome.const import (
DEVICE_CLASS_WINDOW, DEVICE_CLASS_WINDOW,
) )
from esphome.core import CORE, coroutine_with_priority 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 from esphome.util import Registry
CODEOWNERS = ["@esphome/core"] 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 import automation
from esphome.automation import maybe_simple_id from esphome.automation import maybe_simple_id
import esphome.codegen as cg
from esphome.components import mqtt, web_server from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_DEVICE_CLASS, CONF_DEVICE_CLASS,
CONF_ENTITY_CATEGORY, CONF_ENTITY_CATEGORY,
CONF_ICON, CONF_ICON,
CONF_ID, CONF_ID,
CONF_MQTT_ID,
CONF_ON_PRESS, CONF_ON_PRESS,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_MQTT_ID,
CONF_WEB_SERVER_ID, CONF_WEB_SERVER_ID,
DEVICE_CLASS_EMPTY, DEVICE_CLASS_EMPTY,
DEVICE_CLASS_IDENTIFY, DEVICE_CLASS_IDENTIFY,
@ -18,8 +18,8 @@ from esphome.const import (
DEVICE_CLASS_UPDATE, DEVICE_CLASS_UPDATE,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
IS_PLATFORM_COMPONENT = True 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 from esphome import automation
import esphome.codegen as cg
from esphome.components import mqtt, web_server from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_ACTION_STATE_TOPIC, CONF_ACTION_STATE_TOPIC,
CONF_AWAY, CONF_AWAY,
@ -21,6 +20,7 @@ from esphome.const import (
CONF_MODE, CONF_MODE,
CONF_MODE_COMMAND_TOPIC, CONF_MODE_COMMAND_TOPIC,
CONF_MODE_STATE_TOPIC, CONF_MODE_STATE_TOPIC,
CONF_MQTT_ID,
CONF_ON_CONTROL, CONF_ON_CONTROL,
CONF_ON_STATE, CONF_ON_STATE,
CONF_PRESET, CONF_PRESET,
@ -33,20 +33,20 @@ from esphome.const import (
CONF_TARGET_HUMIDITY_STATE_TOPIC, CONF_TARGET_HUMIDITY_STATE_TOPIC,
CONF_TARGET_TEMPERATURE, CONF_TARGET_TEMPERATURE,
CONF_TARGET_TEMPERATURE_COMMAND_TOPIC, CONF_TARGET_TEMPERATURE_COMMAND_TOPIC,
CONF_TARGET_TEMPERATURE_STATE_TOPIC,
CONF_TARGET_TEMPERATURE_HIGH, CONF_TARGET_TEMPERATURE_HIGH,
CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC, CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC,
CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC, CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC,
CONF_TARGET_TEMPERATURE_LOW, CONF_TARGET_TEMPERATURE_LOW,
CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC, CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC,
CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC, CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC,
CONF_TARGET_TEMPERATURE_STATE_TOPIC,
CONF_TEMPERATURE_STEP, CONF_TEMPERATURE_STEP,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_VISUAL, CONF_VISUAL,
CONF_MQTT_ID,
CONF_WEB_SERVER_ID, CONF_WEB_SERVER_ID,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True

View file

@ -141,7 +141,7 @@ struct ClimateDeviceRestoreState {
float target_temperature_low; float target_temperature_low;
float target_temperature_high; float target_temperature_high;
}; };
}; } __attribute__((packed));
float target_humidity; float target_humidity;
/// Convert this struct to a climate call that can be performed. /// 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 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 from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_ID,
CONF_DEVICE_CLASS, CONF_DEVICE_CLASS,
CONF_STATE, CONF_ID,
CONF_MQTT_ID,
CONF_ON_OPEN, CONF_ON_OPEN,
CONF_POSITION, CONF_POSITION,
CONF_POSITION_COMMAND_TOPIC, CONF_POSITION_COMMAND_TOPIC,
CONF_POSITION_STATE_TOPIC, CONF_POSITION_STATE_TOPIC,
CONF_STATE,
CONF_STOP,
CONF_TILT, CONF_TILT,
CONF_TILT_COMMAND_TOPIC, CONF_TILT_COMMAND_TOPIC,
CONF_TILT_STATE_TOPIC, CONF_TILT_STATE_TOPIC,
CONF_STOP,
CONF_MQTT_ID,
CONF_WEB_SERVER_ID,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_WEB_SERVER_ID,
DEVICE_CLASS_AWNING, DEVICE_CLASS_AWNING,
DEVICE_CLASS_BLIND, DEVICE_CLASS_BLIND,
DEVICE_CLASS_CURTAIN, DEVICE_CLASS_CURTAIN,

View file

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

View file

@ -35,7 +35,7 @@ class CST226Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice
void continue_setup_(); void continue_setup_();
InternalGPIOPin *interrupt_pin_{}; InternalGPIOPin *interrupt_pin_{};
GPIOPin *reset_pin_{NULL_PIN}; GPIOPin *reset_pin_{};
uint8_t chip_id_{}; uint8_t chip_id_{};
bool setup_complete_{}; 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 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 ( from esphome.const import (
CONF_DATE,
CONF_DATETIME,
CONF_DAY,
CONF_HOUR,
CONF_ID, CONF_ID,
CONF_MINUTE,
CONF_MONTH,
CONF_MQTT_ID,
CONF_ON_TIME, CONF_ON_TIME,
CONF_ON_VALUE, CONF_ON_VALUE,
CONF_SECOND,
CONF_TIME,
CONF_TIME_ID, CONF_TIME_ID,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_TYPE, CONF_TYPE,
CONF_MQTT_ID,
CONF_WEB_SERVER_ID, CONF_WEB_SERVER_ID,
CONF_DATE,
CONF_DATETIME,
CONF_TIME,
CONF_YEAR, CONF_YEAR,
CONF_MONTH,
CONF_DAY,
CONF_SECOND,
CONF_HOUR,
CONF_MINUTE,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@rfdarter", "@jesserockz"] CODEOWNERS = ["@rfdarter", "@jesserockz"]
DEPENDENCIES = ["time"] DEPENDENCIES = ["time"]

View file

@ -1,23 +1,26 @@
import re import re
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation, core 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 ( from esphome.const import (
CONF_ID, CONF_ACTIVE,
CONF_TYPE,
CONF_TRIGGER_ID,
CONF_ON_VALUE,
CONF_COMMAND, CONF_COMMAND,
CONF_CUSTOM, CONF_CUSTOM,
CONF_NUMBER,
CONF_FORMAT, CONF_FORMAT,
CONF_ID,
CONF_ITEMS,
CONF_MODE, 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"] CODEOWNERS = ["@numo68"]
@ -29,10 +32,8 @@ CONF_JOYSTICK = "joystick"
CONF_LABEL = "label" CONF_LABEL = "label"
CONF_MENU = "menu" CONF_MENU = "menu"
CONF_BACK = "back" CONF_BACK = "back"
CONF_TEXT = "text"
CONF_SELECT = "select" CONF_SELECT = "select"
CONF_SWITCH = "switch" CONF_SWITCH = "switch"
CONF_ITEMS = "items"
CONF_ON_TEXT = "on_text" CONF_ON_TEXT = "on_text"
CONF_OFF_TEXT = "off_text" CONF_OFF_TEXT = "off_text"
CONF_VALUE_LAMBDA = "value_lambda" 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 from esphome import automation
import esphome.codegen as cg
from esphome.components import mqtt from esphome.components import mqtt
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_DEVICE_CLASS, CONF_DEVICE_CLASS,
CONF_ENTITY_CATEGORY, CONF_ENTITY_CATEGORY,
CONF_EVENT_TYPE,
CONF_ICON, CONF_ICON,
CONF_ID, CONF_ID,
CONF_MQTT_ID,
CONF_ON_EVENT, CONF_ON_EVENT,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_MQTT_ID,
CONF_EVENT_TYPE,
DEVICE_CLASS_BUTTON, DEVICE_CLASS_BUTTON,
DEVICE_CLASS_DOORBELL, DEVICE_CLASS_DOORBELL,
DEVICE_CLASS_EMPTY, DEVICE_CLASS_EMPTY,
DEVICE_CLASS_MOTION, DEVICE_CLASS_MOTION,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@nohat"] CODEOWNERS = ["@nohat"]
IS_PLATFORM_COMPONENT = True 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 import automation
from esphome.automation import maybe_simple_id from esphome.automation import maybe_simple_id
import esphome.codegen as cg
from esphome.components import mqtt, web_server from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_DIRECTION,
CONF_ID, CONF_ID,
CONF_MQTT_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_OFF_SPEED_CYCLE,
CONF_ON_DIRECTION_SET, CONF_ON_DIRECTION_SET,
CONF_ON_OSCILLATING_SET, CONF_ON_OSCILLATING_SET,
CONF_ON_PRESET_SET,
CONF_ON_SPEED_SET, CONF_ON_SPEED_SET,
CONF_ON_STATE, CONF_ON_STATE,
CONF_ON_TURN_OFF, CONF_ON_TURN_OFF,
CONF_ON_TURN_ON, CONF_ON_TURN_ON,
CONF_ON_PRESET_SET, CONF_OSCILLATING,
CONF_TRIGGER_ID, CONF_OSCILLATION_COMMAND_TOPIC,
CONF_DIRECTION, CONF_OSCILLATION_STATE_TOPIC,
CONF_RESTORE_MODE, 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.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity from esphome.cpp_helpers import setup_entity

View file

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

View file

@ -39,8 +39,8 @@ bool HX711Sensor::read_sensor_(uint32_t *result) {
return false; return false;
} }
this->status_clear_warning();
uint32_t data = 0; uint32_t data = 0;
bool final_dout;
{ {
InterruptLock lock; InterruptLock lock;
@ -59,8 +59,17 @@ bool HX711Sensor::read_sensor_(uint32_t *result) {
this->sck_pin_->digital_write(false); this->sck_pin_->digital_write(false);
delayMicroseconds(1); 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) { if (data & 0x800000ULL) {
data |= 0xFF000000ULL; data |= 0xFF000000ULL;
} }

View file

@ -233,6 +233,7 @@ void I2SAudioSpeaker::loop() {
switch (this->state_) { switch (this->state_) {
case speaker::STATE_STARTING: case speaker::STATE_STARTING:
this->start_(); this->start_();
[[fallthrough]];
case speaker::STATE_RUNNING: case speaker::STATE_RUNNING:
case speaker::STATE_STOPPING: case speaker::STATE_STOPPING:
this->watch_(); 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.automation as auto
import esphome.codegen as cg
from esphome.components import mqtt, power_supply, web_server from esphome.components import mqtt, power_supply, web_server
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_COLD_WHITE_COLOR_TEMPERATURE,
CONF_COLOR_CORRECT, CONF_COLOR_CORRECT,
CONF_DEFAULT_TRANSITION_LENGTH, CONF_DEFAULT_TRANSITION_LENGTH,
CONF_EFFECTS, CONF_EFFECTS,
@ -10,36 +11,36 @@ from esphome.const import (
CONF_GAMMA_CORRECT, CONF_GAMMA_CORRECT,
CONF_ID, CONF_ID,
CONF_MQTT_ID, CONF_MQTT_ID,
CONF_WEB_SERVER_ID, CONF_ON_STATE,
CONF_POWER_SUPPLY,
CONF_RESTORE_MODE,
CONF_ON_TURN_OFF, CONF_ON_TURN_OFF,
CONF_ON_TURN_ON, CONF_ON_TURN_ON,
CONF_ON_STATE, CONF_POWER_SUPPLY,
CONF_RESTORE_MODE,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_COLD_WHITE_COLOR_TEMPERATURE,
CONF_WARM_WHITE_COLOR_TEMPERATURE, CONF_WARM_WHITE_COLOR_TEMPERATURE,
CONF_WEB_SERVER_ID,
) )
from esphome.core import coroutine_with_priority from esphome.core import coroutine_with_priority
from esphome.cpp_helpers import setup_entity from esphome.cpp_helpers import setup_entity
from .automation import light_control_to_code # noqa from .automation import light_control_to_code # noqa
from .effects import ( from .effects import (
validate_effects, ADDRESSABLE_EFFECTS,
BINARY_EFFECTS, BINARY_EFFECTS,
EFFECTS_REGISTRY,
MONOCHROMATIC_EFFECTS, MONOCHROMATIC_EFFECTS,
RGB_EFFECTS, RGB_EFFECTS,
ADDRESSABLE_EFFECTS, validate_effects,
EFFECTS_REGISTRY,
) )
from .types import ( # noqa from .types import ( # noqa
LightState,
AddressableLightState,
light_ns,
LightOutput,
AddressableLight, AddressableLight,
LightTurnOnTrigger, AddressableLightState,
LightTurnOffTrigger, LightOutput,
LightState,
LightStateTrigger, LightStateTrigger,
LightTurnOffTrigger,
LightTurnOnTrigger,
light_ns,
) )
CODEOWNERS = ["@esphome/core"] 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 import automation
from esphome.automation import Condition, maybe_simple_id from esphome.automation import Condition, maybe_simple_id
import esphome.codegen as cg
from esphome.components import mqtt, web_server from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_ID, CONF_ID,
CONF_MQTT_ID,
CONF_ON_LOCK, CONF_ON_LOCK,
CONF_ON_UNLOCK, CONF_ON_UNLOCK,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_MQTT_ID,
CONF_WEB_SERVER_ID, CONF_WEB_SERVER_ID,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority

View file

@ -1,9 +1,21 @@
import re import re
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation from esphome import automation
from esphome.automation import LambdaAction 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 ( from esphome.const import (
CONF_ARGS, CONF_ARGS,
CONF_BAUD_RATE, CONF_BAUD_RATE,
@ -18,27 +30,12 @@ from esphome.const import (
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_TX_BUFFER_SIZE, CONF_TX_BUFFER_SIZE,
PLATFORM_BK72XX, PLATFORM_BK72XX,
PLATFORM_RTL87XX,
PLATFORM_ESP32, PLATFORM_ESP32,
PLATFORM_ESP8266, PLATFORM_ESP8266,
PLATFORM_RP2040, PLATFORM_RP2040,
PLATFORM_RTL87XX,
) )
from esphome.core import CORE, EsphomeError, Lambda, coroutine_with_priority 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"] CODEOWNERS = ["@esphome/core"]
logger_ns = cg.esphome_ns.namespace("logger") 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 . import defines as df, helpers, lv_validation as lvalid
from .automation import disp_update, update_to_code from .automation import disp_update, update_to_code
from .defines import CONF_SKIP from .defines import CONF_SKIP
from .encoders import ENCODERS_CONFIG, encoders_to_code
from .lv_validation import lv_bool, lv_images_used from .lv_validation import lv_bool, lv_images_used
from .lvcode import LvContext, LvglComponent from .lvcode import LvContext, LvglComponent
from .rotary_encoders import ROTARY_ENCODER_CONFIG, rotary_encoders_to_code
from .schemas import ( from .schemas import (
DISP_BG_SCHEMA, DISP_BG_SCHEMA,
FLEX_OBJ_SCHEMA, FLEX_OBJ_SCHEMA,
@ -256,7 +256,7 @@ async def to_code(config):
async with LvContext(lv_component): async with LvContext(lv_component):
await touchscreens_to_code(lv_component, config) 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 theme_to_code(config)
await styles_to_code(config) await styles_to_code(config)
await set_obj_properties(lv_scr_act, 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.Optional(name): obj_schema(w) for name, w in WIDGET_TYPES.items()}
), ),
cv.GenerateID(df.CONF_TOUCHSCREENS): touchscreen_schema, 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) .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 import codegen as cg, config_validation as cv
from esphome.const import CONF_ITEMS
from esphome.core import ID, Lambda from esphome.core import ID, Lambda
from esphome.cpp_generator import MockObj from esphome.cpp_generator import MockObj
from esphome.cpp_types import uint32 from esphome.cpp_types import uint32
@ -115,7 +116,6 @@ CONF_SCROLLBAR = "scrollbar"
CONF_INDICATOR = "indicator" CONF_INDICATOR = "indicator"
CONF_KNOB = "knob" CONF_KNOB = "knob"
CONF_SELECTED = "selected" CONF_SELECTED = "selected"
CONF_ITEMS = "items"
CONF_TICKS = "ticks" CONF_TICKS = "ticks"
CONF_CURSOR = "cursor" CONF_CURSOR = "cursor"
CONF_TEXTAREA_PLACEHOLDER = "textarea_placeholder" CONF_TEXTAREA_PLACEHOLDER = "textarea_placeholder"
@ -388,6 +388,7 @@ CONF_DEFAULT = "default"
CONF_DEFAULT_FONT = "default_font" CONF_DEFAULT_FONT = "default_font"
CONF_DIR = "dir" CONF_DIR = "dir"
CONF_DISPLAYS = "displays" CONF_DISPLAYS = "displays"
CONF_ENCODERS = "encoders"
CONF_END_ANGLE = "end_angle" CONF_END_ANGLE = "end_angle"
CONF_END_VALUE = "end_value" CONF_END_VALUE = "end_value"
CONF_ENTER_BUTTON = "enter_button" CONF_ENTER_BUTTON = "enter_button"
@ -441,7 +442,6 @@ CONF_RECOLOR = "recolor"
CONF_RIGHT_BUTTON = "right_button" CONF_RIGHT_BUTTON = "right_button"
CONF_ROLLOVER = "rollover" CONF_ROLLOVER = "rollover"
CONF_ROOT_BACK_BTN = "root_back_btn" CONF_ROOT_BACK_BTN = "root_back_btn"
CONF_ROTARY_ENCODERS = "rotary_encoders"
CONF_ROWS = "rows" CONF_ROWS = "rows"
CONF_SCALE_LINES = "scale_lines" CONF_SCALE_LINES = "scale_lines"
CONF_SCROLLBAR_MODE = "scrollbar_mode" CONF_SCROLLBAR_MODE = "scrollbar_mode"
@ -460,7 +460,6 @@ CONF_SKIP = "skip"
CONF_SYMBOL = "symbol" CONF_SYMBOL = "symbol"
CONF_TAB_ID = "tab_id" CONF_TAB_ID = "tab_id"
CONF_TABS = "tabs" CONF_TABS = "tabs"
CONF_TEXT = "text"
CONF_TILE = "tile" CONF_TILE = "tile"
CONF_TILE_ID = "tile_id" CONF_TILE_ID = "tile_id"
CONF_TILES = "tiles" 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 esphome.const import CONF_GROUP, CONF_ID, CONF_SENSOR
from .defines import ( from .defines import (
CONF_ENCODERS,
CONF_ENTER_BUTTON, CONF_ENTER_BUTTON,
CONF_LEFT_BUTTON, CONF_LEFT_BUTTON,
CONF_LONG_PRESS_REPEAT_TIME, CONF_LONG_PRESS_REPEAT_TIME,
CONF_LONG_PRESS_TIME, CONF_LONG_PRESS_TIME,
CONF_RIGHT_BUTTON, CONF_RIGHT_BUTTON,
CONF_ROTARY_ENCODERS,
) )
from .helpers import lvgl_components_required from .helpers import lvgl_components_required, requires_component
from .lvcode import lv, lv_add, lv_expr from .lvcode import lv, lv_add, lv_assign, lv_expr, lv_Pvariable
from .schemas import ENCODER_SCHEMA from .schemas import ENCODER_SCHEMA
from .types import lv_indev_type_t from .types import lv_group_t, lv_indev_type_t
from .widgets import add_group
ROTARY_ENCODER_CONFIG = cv.ensure_list( ENCODERS_CONFIG = cv.ensure_list(
ENCODER_SCHEMA.extend( ENCODER_SCHEMA.extend(
{ {
cv.Required(CONF_ENTER_BUTTON): cv.use_id(BinarySensor), cv.Required(CONF_ENTER_BUTTON): cv.use_id(BinarySensor),
cv.Required(CONF_SENSOR): cv.Any( cv.Required(CONF_SENSOR): cv.Any(
cv.use_id(RotaryEncoderSensor), cv.All(
cv.use_id(RotaryEncoderSensor), requires_component("rotary_encoder")
),
cv.Schema( cv.Schema(
{ {
cv.Required(CONF_LEFT_BUTTON): cv.use_id(BinarySensor), 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): async def encoders_to_code(var, config):
for enc_conf in config.get(CONF_ROTARY_ENCODERS, ()): for enc_conf in config.get(CONF_ENCODERS, ()):
lvgl_components_required.add("KEY_LISTENER") lvgl_components_required.add("KEY_LISTENER")
lvgl_components_required.add("ROTARY_ENCODER")
lpt = enc_conf[CONF_LONG_PRESS_TIME].total_milliseconds lpt = enc_conf[CONF_LONG_PRESS_TIME].total_milliseconds
lprt = enc_conf[CONF_LONG_PRESS_REPEAT_TIME].total_milliseconds lprt = enc_conf[CONF_LONG_PRESS_REPEAT_TIME].total_milliseconds
listener = cg.new_Pvariable( listener = cg.new_Pvariable(
@ -57,7 +57,9 @@ async def rotary_encoders_to_code(var, config):
lv_add(listener.set_sensor(sensor_config)) lv_add(listener.set_sensor(sensor_config))
b_sensor = await cg.get_variable(enc_conf[CONF_ENTER_BUTTON]) b_sensor = await cg.get_variable(enc_conf[CONF_ENTER_BUTTON])
cg.add(listener.set_enter_button(b_sensor)) 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) lv.indev_set_group(lv_expr.indev_drv_register(listener.get_drv()), group)
else: else:
lv.indev_drv_register(listener.get_drv()) 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 #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) { LVEncoderListener::LVEncoderListener(lv_indev_type_t type, uint16_t lpt, uint16_t lprt) {
lv_indev_drv_init(&this->drv_); lv_indev_drv_init(&this->drv_);
this->drv_.type = type; this->drv_.type = type;
@ -143,7 +143,7 @@ LVEncoderListener::LVEncoderListener(lv_indev_type_t type, uint16_t lpt, uint16_
data->continue_reading = false; data->continue_reading = false;
}; };
} }
#endif // USE_LVGL_ROTARY_ENCODER #endif // USE_LVGL_KEY_LISTENER
#ifdef USE_LVGL_BUTTONMATRIX #ifdef USE_LVGL_BUTTONMATRIX
void LvButtonMatrixType::set_obj(lv_obj_t *lv_obj) { void LvButtonMatrixType::set_obj(lv_obj_t *lv_obj) {

View file

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

View file

@ -7,6 +7,7 @@ from esphome.const import (
CONF_ID, CONF_ID,
CONF_ON_VALUE, CONF_ON_VALUE,
CONF_STATE, CONF_STATE,
CONF_TEXT,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_TYPE, 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 . import defines as df, lv_validation as lvalid, types as ty
from .helpers import add_lv_use, requires_component, validate_printf 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 .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. # this will be populated later, in __init__.py to avoid circular imports.
WIDGET_TYPES: dict = {} WIDGET_TYPES: dict = {}
@ -25,7 +26,7 @@ WIDGET_TYPES: dict = {}
# A schema for text properties # A schema for text properties
TEXT_SCHEMA = cv.Schema( TEXT_SCHEMA = cv.Schema(
{ {
cv.Optional(df.CONF_TEXT): cv.Any( cv.Optional(CONF_TEXT): cv.Any(
cv.All( cv.All(
cv.Schema( cv.Schema(
{ {
@ -60,7 +61,7 @@ ENCODER_SCHEMA = cv.Schema(
cv.GenerateID(): cv.All( cv.GenerateID(): cv.All(
cv.declare_id(ty.LVEncoderListener), requires_component("binary_sensor") 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_TIME, default="400ms"): PRESS_TIME,
cv.Optional(df.CONF_LONG_PRESS_REPEAT_TIME, default="100ms"): 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.Schema(
{ {
cv.Optional(CONF_STATE): SET_STATE_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 # A style schema that can include text
STYLED_TEXT_SCHEMA = cv.maybe_simple_value( 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 # For use by platform components

View file

@ -1,10 +1,10 @@
import sys import sys
from esphome import automation, codegen as cg 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 esphome.cpp_generator import MockObj, MockObjClass
from .defines import CONF_TEXT, lvgl_ns from .defines import lvgl_ns
from .lvcode import lv_expr 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.const import CONF_GROUP, CONF_ID, CONF_STATE, CONF_TYPE
from esphome.core import ID, TimePeriod from esphome.core import ID, TimePeriod
from esphome.coroutine import FakeAwaitable from esphome.coroutine import FakeAwaitable
from esphome.cpp_generator import AssignmentExpression, CallExpression, MockObj from esphome.cpp_generator import CallExpression, MockObj
from ..defines import ( from ..defines import (
CONF_DEFAULT, CONF_DEFAULT,
@ -44,15 +44,7 @@ from ..lvcode import (
lv_Pvariable, lv_Pvariable,
) )
from ..schemas import ALL_STYLES, STYLE_REMAP, WIDGET_TYPES from ..schemas import ALL_STYLES, STYLE_REMAP, WIDGET_TYPES
from ..types import ( from ..types import LV_STATE, LvType, WidgetType, lv_coord_t, lv_obj_t, lv_obj_t_ptr
LV_STATE,
LvType,
WidgetType,
lv_coord_t,
lv_group_t,
lv_obj_t,
lv_obj_t_ptr,
)
EVENT_LAMB = "event_lamb__" EVENT_LAMB = "event_lamb__"
@ -317,7 +309,8 @@ async def set_obj_properties(w: Widget, config):
value = await ALL_STYLES[prop].process(value) value = await ALL_STYLES[prop].process(value)
prop_r = STYLE_REMAP.get(prop, prop) prop_r = STYLE_REMAP.get(prop, prop)
w.set_style(prop_r, value, lv_state) 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) lv.group_add_obj(group, w.obj)
flag_clr = set() flag_clr = set()
flag_set = 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_spec = LvScrActType()
lv_scr_act = Widget.create(None, literal("lv_scr_act()"), lv_scr_act_spec, {}) 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 import esphome.codegen as cg
from esphome.components.key_provider import KeyProvider from esphome.components.key_provider import KeyProvider
import esphome.config_validation as cv 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 esphome.cpp_generator import MockObj
from ..automation import action_to_code from ..automation import action_to_code
@ -10,13 +10,11 @@ from ..defines import (
BUTTONMATRIX_CTRLS, BUTTONMATRIX_CTRLS,
CONF_BUTTONS, CONF_BUTTONS,
CONF_CONTROL, CONF_CONTROL,
CONF_ITEMS,
CONF_KEY_CODE, CONF_KEY_CODE,
CONF_MAIN, CONF_MAIN,
CONF_ONE_CHECKED, CONF_ONE_CHECKED,
CONF_ROWS, CONF_ROWS,
CONF_SELECTED, CONF_SELECTED,
CONF_TEXT,
) )
from ..helpers import lvgl_components_required from ..helpers import lvgl_components_required
from ..lv_validation import key_code, lv_bool 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 ..lv_validation import lv_text
from ..lvcode import lv from ..lvcode import lv
from ..schemas import TEXT_SCHEMA from ..schemas import TEXT_SCHEMA

View file

@ -1,9 +1,9 @@
from esphome.components.key_provider import KeyProvider from esphome.components.key_provider import KeyProvider
import esphome.config_validation as cv 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 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 ..helpers import add_lv_use, lvgl_components_required
from ..types import LvCompound, LvType from ..types import LvCompound, LvType
from . import Widget, WidgetType, get_widgets from . import Widget, WidgetType, get_widgets

View file

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

View file

@ -1,5 +1,5 @@
from esphome import config_validation as cv 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.core import ID
from esphome.cpp_generator import new_Pvariable, static_const_array from esphome.cpp_generator import new_Pvariable, static_const_array
from esphome.cpp_types import nullptr from esphome.cpp_types import nullptr
@ -9,7 +9,6 @@ from ..defines import (
CONF_BUTTONS, CONF_BUTTONS,
CONF_CLOSE_BUTTON, CONF_CLOSE_BUTTON,
CONF_MSGBOXES, CONF_MSGBOXES,
CONF_TEXT,
CONF_TITLE, CONF_TITLE,
TYPE_FLEX, TYPE_FLEX,
literal, literal,

View file

@ -1,5 +1,5 @@
import esphome.config_validation as cv 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 ( from ..defines import (
CONF_ACCEPTED_CHARS, CONF_ACCEPTED_CHARS,
@ -10,7 +10,6 @@ from ..defines import (
CONF_PLACEHOLDER_TEXT, CONF_PLACEHOLDER_TEXT,
CONF_SCROLLBAR, CONF_SCROLLBAR,
CONF_SELECTED, CONF_SELECTED,
CONF_TEXT,
CONF_TEXTAREA_PLACEHOLDER, CONF_TEXTAREA_PLACEHOLDER,
) )
from ..lv_validation import lv_bool, lv_int, lv_text from ..lv_validation import lv_bool, lv_int, lv_text

View file

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

View file

@ -1,20 +1,18 @@
from esphome import automation from esphome import automation
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.automation import maybe_simple_id from esphome.automation import maybe_simple_id
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_ID, CONF_ID,
CONF_ON_IDLE,
CONF_ON_STATE, CONF_ON_STATE,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_VOLUME, CONF_VOLUME,
CONF_ON_IDLE,
) )
from esphome.core import CORE from esphome.core import CORE
from esphome.coroutine import coroutine_with_priority from esphome.coroutine import coroutine_with_priority
from esphome.cpp_helpers import setup_entity from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@jesserockz"] CODEOWNERS = ["@jesserockz"]
IS_PLATFORM_COMPONENT = True 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 import automation
from esphome.components import mqtt import esphome.codegen as cg
from esphome.components import web_server from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_ABOVE, CONF_ABOVE,
CONF_BELOW, CONF_BELOW,
CONF_CYCLE,
CONF_DEVICE_CLASS, CONF_DEVICE_CLASS,
CONF_ENTITY_CATEGORY, CONF_ENTITY_CATEGORY,
CONF_ID,
CONF_ICON, CONF_ICON,
CONF_ID,
CONF_MODE, CONF_MODE,
CONF_MQTT_ID,
CONF_ON_VALUE, CONF_ON_VALUE,
CONF_ON_VALUE_RANGE, CONF_ON_VALUE_RANGE,
CONF_OPERATION,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_UNIT_OF_MEASUREMENT, CONF_UNIT_OF_MEASUREMENT,
CONF_MQTT_ID,
CONF_VALUE, CONF_VALUE,
CONF_OPERATION,
CONF_CYCLE,
CONF_WEB_SERVER_ID, CONF_WEB_SERVER_ID,
DEVICE_CLASS_APPARENT_POWER, DEVICE_CLASS_APPARENT_POWER,
DEVICE_CLASS_AQI, DEVICE_CLASS_AQI,
@ -72,8 +71,8 @@ from esphome.const import (
DEVICE_CLASS_WIND_SPEED, DEVICE_CLASS_WIND_SPEED,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
DEVICE_CLASSES = [ 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 #ifdef USE_ESP32
void configure_rmt_(); void configure_rmt_();
uint32_t current_carrier_frequency_{UINT32_MAX}; uint32_t current_carrier_frequency_{38000};
bool initialized_{false}; bool initialized_{false};
std::vector<rmt_item32_t> rmt_temp_; std::vector<rmt_item32_t> rmt_temp_;
esp_err_t error_code_{ESP_OK}; 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 from esphome import automation
import esphome.codegen as cg
from esphome.components import mqtt, web_server from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_CYCLE,
CONF_ENTITY_CATEGORY, CONF_ENTITY_CATEGORY,
CONF_ICON, CONF_ICON,
CONF_ID, CONF_ID,
CONF_INDEX,
CONF_MODE,
CONF_MQTT_ID,
CONF_ON_VALUE, CONF_ON_VALUE,
CONF_OPERATION,
CONF_OPTION, CONF_OPTION,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_MQTT_ID,
CONF_WEB_SERVER_ID, CONF_WEB_SERVER_ID,
CONF_CYCLE,
CONF_MODE,
CONF_OPERATION,
CONF_INDEX,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObjClass

View file

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

View file

@ -7,10 +7,6 @@ namespace spi {
const char *const TAG = "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; } bool SPIDelegate::is_ready() { return true; }
GPIOPin *const NullPin::NULL_PIN = new NullPin(); // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) 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); } 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); } 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 // 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. // a thin wrapper over SPIClass.
class SPIDelegate { class SPIDelegate {
@ -250,21 +248,6 @@ class SPIDelegate {
uint32_t data_rate_{1000000}; uint32_t data_rate_{1000000};
SPIMode mode_{MODE0}; SPIMode mode_{MODE0};
GPIOPin *cs_pin_{NullPin::NULL_PIN}; 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() { virtual void spi_teardown() {
this->parent_->unregister_device(this); this->parent_->unregister_device(this);
this->delegate_ = SPIDelegate::NULL_DELEGATE; this->delegate_ = nullptr;
} }
bool spi_is_ready() { return this->delegate_->is_ready(); } bool spi_is_ready() { return this->delegate_->is_ready(); }
@ -393,7 +376,7 @@ class SPIClient {
uint32_t data_rate_{1000000}; uint32_t data_rate_{1000000};
SPIComponent *parent_{nullptr}; SPIComponent *parent_{nullptr};
GPIOPin *cs_{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; return;
} }
auto call = this->valve_[valve_number.value()].run_duration_number->make_call(); 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); call.set_value(run_duration.value() / 60.0);
} else { } else {
call.set_value(run_duration.value()); call.set_value(run_duration.value());
@ -729,7 +729,7 @@ uint32_t Sprinkler::valve_run_duration(const size_t valve_number) {
return 0; return 0;
} }
if (this->valve_[valve_number].run_duration_number != nullptr) { 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)); return static_cast<uint32_t>(roundf(this->valve_[valve_number].run_duration_number->state * 60));
} else { } else {
return static_cast<uint32_t>(roundf(this->valve_[valve_number].run_duration_number->state)); return static_cast<uint32_t>(roundf(this->valve_[valve_number].run_duration_number->state));

View file

@ -11,7 +11,7 @@
namespace esphome { namespace esphome {
namespace sprinkler { namespace sprinkler {
const std::string min_str = "min"; const std::string MIN_STR = "min";
enum SprinklerState : uint8_t { enum SprinklerState : uint8_t {
// NOTE: these states are used by both SprinklerValveOperator and Sprinkler (the controller)! // 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 import automation
from esphome.automation import Condition, maybe_simple_id from esphome.automation import Condition, maybe_simple_id
import esphome.codegen as cg
from esphome.components import mqtt, web_server from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_DEVICE_CLASS, CONF_DEVICE_CLASS,
CONF_ENTITY_CATEGORY, CONF_ENTITY_CATEGORY,
@ -10,11 +10,11 @@ from esphome.const import (
CONF_ID, CONF_ID,
CONF_INVERTED, CONF_INVERTED,
CONF_MQTT_ID, CONF_MQTT_ID,
CONF_WEB_SERVER_ID,
CONF_ON_TURN_OFF, CONF_ON_TURN_OFF,
CONF_ON_TURN_ON, CONF_ON_TURN_ON,
CONF_RESTORE_MODE, CONF_RESTORE_MODE,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_WEB_SERVER_ID,
DEVICE_CLASS_EMPTY, DEVICE_CLASS_EMPTY,
DEVICE_CLASS_OUTLET, DEVICE_CLASS_OUTLET,
DEVICE_CLASS_SWITCH, DEVICE_CLASS_SWITCH,

View file

@ -1,18 +1,18 @@
from typing import Optional from typing import Optional
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation from esphome import automation
import esphome.codegen as cg
from esphome.components import mqtt, web_server from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_ID, CONF_ID,
CONF_MODE, CONF_MODE,
CONF_MQTT_ID,
CONF_ON_VALUE, CONF_ON_VALUE,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_MQTT_ID,
CONF_WEB_SERVER_ID,
CONF_VALUE, CONF_VALUE,
CONF_WEB_SERVER_ID,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity 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 from esphome import automation
import esphome.codegen as cg
from esphome.components import mqtt, web_server from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_DEVICE_CLASS, CONF_DEVICE_CLASS,
CONF_ENTITY_CATEGORY, CONF_ENTITY_CATEGORY,
CONF_FILTERS, CONF_FILTERS,
CONF_FROM,
CONF_ICON, CONF_ICON,
CONF_ID, CONF_ID,
CONF_ON_VALUE,
CONF_ON_RAW_VALUE,
CONF_TRIGGER_ID,
CONF_MQTT_ID, CONF_MQTT_ID,
CONF_WEB_SERVER_ID, CONF_ON_RAW_VALUE,
CONF_ON_VALUE,
CONF_STATE, CONF_STATE,
CONF_FROM,
CONF_TO, CONF_TO,
CONF_TRIGGER_ID,
CONF_WEB_SERVER_ID,
DEVICE_CLASS_DATE, DEVICE_CLASS_DATE,
DEVICE_CLASS_EMPTY, DEVICE_CLASS_EMPTY,
DEVICE_CLASS_TIMESTAMP, 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 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 from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_DEVICE_CLASS, CONF_DEVICE_CLASS,
CONF_ID, CONF_ID,

View file

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

View file

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

View file

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

View file

@ -62,24 +62,6 @@ class GPIOPin {
virtual bool is_internal() { return false; } 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) /// Copy of GPIOPin that is safe to use from ISRs (with no virtual functions)
class ISRInternalGPIOPin { class ISRInternalGPIOPin {
public: public:

View file

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

View file

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

View file

@ -40,6 +40,7 @@ lib_deps =
wjtje/qr-code-generator-library@1.7.0 ; qr_code wjtje/qr-code-generator-library@1.7.0 ; qr_code
functionpointer/arduino-MLX90393@1.0.0 ; mlx90393 functionpointer/arduino-MLX90393@1.0.0 ; mlx90393
pavlodn/HaierProtocol@0.9.31 ; haier 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 ; 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 https://github.com/Sensirion/arduino-gas-index-algorithm.git#3.2.1 ; Sensirion Gas Index Algorithm Arduino Library
lvgl/lvgl@8.4.0 ; lvgl lvgl/lvgl@8.4.0 ; lvgl

View file

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

View file

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

View file

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

View file

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

View file

@ -1,21 +1,6 @@
#!/usr/bin/env python3 #!/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 argparse
import click
import colorama
import multiprocessing import multiprocessing
import os import os
import queue import queue
@ -26,6 +11,20 @@ import sys
import tempfile import tempfile
import threading 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): def clang_options(idedata):

View file

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

View file

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

View file

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

View file

@ -24,6 +24,33 @@ display:
invert_colors: false invert_colors: false
update_interval: never 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: packages:
lvgl: !include lvgl-package.yaml lvgl: !include lvgl-package.yaml

View file

@ -67,7 +67,7 @@ lvgl:
displays: displays:
- tft_display - tft_display
- second_display - second_display
rotary_encoders: encoders:
sensor: encoder sensor: encoder
enter_button: pushbutton enter_button: pushbutton
group: general 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: