mirror of
https://github.com/esphome/esphome.git
synced 2024-12-26 15:34:53 +01:00
Merge remote-tracking branch 'origin/dev' into nrf52_core
This commit is contained in:
commit
53c9248e40
122 changed files with 2013 additions and 778 deletions
2
.github/workflows/ci-docker.yml
vendored
2
.github/workflows/ci-docker.yml
vendored
|
@ -46,7 +46,7 @@ jobs:
|
|||
with:
|
||||
python-version: "3.9"
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.5.0
|
||||
uses: docker/setup-buildx-action@v3.6.1
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3.2.0
|
||||
|
||||
|
|
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
|
@ -90,7 +90,7 @@ jobs:
|
|||
python-version: "3.9"
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.5.0
|
||||
uses: docker/setup-buildx-action@v3.6.1
|
||||
- name: Set up QEMU
|
||||
if: matrix.platform != 'linux/amd64'
|
||||
uses: docker/setup-qemu-action@v3.2.0
|
||||
|
@ -184,7 +184,7 @@ jobs:
|
|||
merge-multiple: true
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.5.0
|
||||
uses: docker/setup-buildx-action@v3.6.1
|
||||
|
||||
- name: Log in to docker hub
|
||||
if: matrix.registry == 'dockerhub'
|
||||
|
|
|
@ -428,6 +428,7 @@ esphome/components/veml7700/* @latonita
|
|||
esphome/components/version/* @esphome/core
|
||||
esphome/components/voice_assistant/* @jesserockz
|
||||
esphome/components/wake_on_lan/* @clydebarrow @willwill2will54
|
||||
esphome/components/watchdog/* @oarcher
|
||||
esphome/components/waveshare_epaper/* @clydebarrow
|
||||
esphome/components/web_server_base/* @OttoWinter
|
||||
esphome/components/web_server_idf/* @dentra
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
# PYTHON_ARGCOMPLETE_OK
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
import functools
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
import argcomplete
|
||||
|
||||
|
@ -33,21 +33,21 @@ from esphome.const import (
|
|||
PLATFORM_BK72XX,
|
||||
PLATFORM_ESP32,
|
||||
PLATFORM_ESP8266,
|
||||
PLATFORM_NRF52,
|
||||
PLATFORM_RP2040,
|
||||
PLATFORM_RTL87XX,
|
||||
SECRETS_FILES,
|
||||
PLATFORM_NRF52,
|
||||
)
|
||||
from esphome.core import CORE, EsphomeError, coroutine
|
||||
from esphome.helpers import indent, is_ip_address
|
||||
from esphome.log import Fore, color, setup_log
|
||||
from esphome.util import (
|
||||
get_serial_ports,
|
||||
list_yaml_files,
|
||||
run_external_command,
|
||||
run_external_process,
|
||||
safe_print,
|
||||
list_yaml_files,
|
||||
get_serial_ports,
|
||||
)
|
||||
from esphome.log import color, setup_log, Fore
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -117,6 +117,7 @@ def get_port_type(port):
|
|||
|
||||
def run_miniterm(config, port):
|
||||
import serial
|
||||
|
||||
from esphome import platformio_api
|
||||
|
||||
if CONF_LOGGER not in config:
|
||||
|
@ -604,9 +605,10 @@ def command_update_all(args):
|
|||
|
||||
|
||||
def command_idedata(args, config):
|
||||
from esphome import platformio_api
|
||||
import json
|
||||
|
||||
from esphome import platformio_api
|
||||
|
||||
logging.disable(logging.INFO)
|
||||
logging.disable(logging.WARNING)
|
||||
|
||||
|
@ -755,7 +757,14 @@ def parse_args(argv):
|
|||
)
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=f"ESPHome v{const.__version__}", parents=[options_parser]
|
||||
description=f"ESPHome {const.__version__}", parents=[options_parser]
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--version",
|
||||
action="version",
|
||||
version=f"Version: {const.__version__}",
|
||||
help="Print the ESPHome version and exit.",
|
||||
)
|
||||
|
||||
mqtt_options = argparse.ArgumentParser(add_help=False)
|
||||
|
@ -956,67 +965,6 @@ def parse_args(argv):
|
|||
# a deprecation warning).
|
||||
arguments = argv[1:]
|
||||
|
||||
# On Python 3.9+ we can simply set exit_on_error=False in the constructor
|
||||
def _raise(x):
|
||||
raise argparse.ArgumentError(None, x)
|
||||
|
||||
# First, try new-style parsing, but don't exit in case of failure
|
||||
try:
|
||||
# duplicate parser so that we can use the original one to raise errors later on
|
||||
current_parser = argparse.ArgumentParser(add_help=False, parents=[parser])
|
||||
current_parser.set_defaults(deprecated_argv_suggestion=None)
|
||||
current_parser.error = _raise
|
||||
return current_parser.parse_args(arguments)
|
||||
except argparse.ArgumentError:
|
||||
pass
|
||||
|
||||
# Second, try compat parsing and rearrange the command-line if it succeeds
|
||||
# Disable argparse's built-in help option and add it manually to prevent this
|
||||
# parser from printing the help messagefor the old format when invoked with -h.
|
||||
compat_parser = argparse.ArgumentParser(parents=[options_parser], add_help=False)
|
||||
compat_parser.add_argument("-h", "--help", action="store_true")
|
||||
compat_parser.add_argument("configuration", nargs="*")
|
||||
compat_parser.add_argument(
|
||||
"command",
|
||||
choices=[
|
||||
"config",
|
||||
"compile",
|
||||
"upload",
|
||||
"logs",
|
||||
"run",
|
||||
"clean-mqtt",
|
||||
"wizard",
|
||||
"mqtt-fingerprint",
|
||||
"version",
|
||||
"clean",
|
||||
"dashboard",
|
||||
"vscode",
|
||||
"update-all",
|
||||
],
|
||||
)
|
||||
|
||||
try:
|
||||
compat_parser.error = _raise
|
||||
result, unparsed = compat_parser.parse_known_args(argv[1:])
|
||||
last_option = len(arguments) - len(unparsed) - 1 - len(result.configuration)
|
||||
unparsed = [
|
||||
"--device" if arg in ("--upload-port", "--serial-port") else arg
|
||||
for arg in unparsed
|
||||
]
|
||||
arguments = (
|
||||
arguments[0:last_option]
|
||||
+ [result.command]
|
||||
+ result.configuration
|
||||
+ unparsed
|
||||
)
|
||||
deprecated_argv_suggestion = arguments
|
||||
except argparse.ArgumentError:
|
||||
# old-style parsing failed, don't suggest any argument
|
||||
deprecated_argv_suggestion = None
|
||||
|
||||
# Finally, run the new-style parser again with the possibly swapped arguments,
|
||||
# and let it error out if the command is unparsable.
|
||||
parser.set_defaults(deprecated_argv_suggestion=deprecated_argv_suggestion)
|
||||
argcomplete.autocomplete(parser)
|
||||
return parser.parse_args(arguments)
|
||||
|
||||
|
@ -1031,13 +979,6 @@ def run_esphome(argv):
|
|||
# Show timestamp for dashboard access logs
|
||||
args.command == "dashboard",
|
||||
)
|
||||
if args.deprecated_argv_suggestion is not None and args.command != "vscode":
|
||||
_LOGGER.warning(
|
||||
"Calling ESPHome with the configuration before the command is deprecated "
|
||||
"and will be removed in the future. "
|
||||
)
|
||||
_LOGGER.warning("Please instead use:")
|
||||
_LOGGER.warning(" esphome %s", " ".join(args.deprecated_argv_suggestion))
|
||||
|
||||
if args.command in PRE_CONFIG_ACTIONS:
|
||||
try:
|
||||
|
|
|
@ -7,10 +7,10 @@ from esphome.const import (
|
|||
CONF_ELSE,
|
||||
CONF_ID,
|
||||
CONF_THEN,
|
||||
CONF_TIME,
|
||||
CONF_TIMEOUT,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_TYPE_ID,
|
||||
CONF_TIME,
|
||||
CONF_UPDATE_INTERVAL,
|
||||
)
|
||||
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
|
||||
|
|
|
@ -8,55 +8,78 @@
|
|||
# want to break suddenly due to a rename (this file will get backports for features).
|
||||
|
||||
# pylint: disable=unused-import
|
||||
from esphome.cpp_generator import ( # noqa
|
||||
from esphome.cpp_generator import ( # noqa: F401
|
||||
ArrayInitializer,
|
||||
Expression,
|
||||
LineComment,
|
||||
MockObj,
|
||||
MockObjClass,
|
||||
Pvariable,
|
||||
RawExpression,
|
||||
RawStatement,
|
||||
TemplateArguments,
|
||||
StructInitializer,
|
||||
ArrayInitializer,
|
||||
safe_exp,
|
||||
Statement,
|
||||
LineComment,
|
||||
progmem_array,
|
||||
static_const_array,
|
||||
statement,
|
||||
variable,
|
||||
with_local_variable,
|
||||
new_variable,
|
||||
Pvariable,
|
||||
new_Pvariable,
|
||||
StructInitializer,
|
||||
TemplateArguments,
|
||||
add,
|
||||
add_global,
|
||||
add_library,
|
||||
add_build_flag,
|
||||
add_define,
|
||||
add_global,
|
||||
add_library,
|
||||
add_platformio_option,
|
||||
get_variable,
|
||||
get_variable_with_full_id,
|
||||
process_lambda,
|
||||
is_template,
|
||||
new_Pvariable,
|
||||
new_variable,
|
||||
process_lambda,
|
||||
progmem_array,
|
||||
safe_exp,
|
||||
statement,
|
||||
static_const_array,
|
||||
templatable,
|
||||
MockObj,
|
||||
MockObjClass,
|
||||
variable,
|
||||
with_local_variable,
|
||||
)
|
||||
from esphome.cpp_helpers import ( # noqa
|
||||
gpio_pin_expression,
|
||||
register_component,
|
||||
from esphome.cpp_helpers import ( # noqa: F401
|
||||
build_registry_entry,
|
||||
build_registry_list,
|
||||
extract_registry_entry_config,
|
||||
register_parented,
|
||||
gpio_pin_expression,
|
||||
past_safe_mode,
|
||||
register_component,
|
||||
register_parented,
|
||||
)
|
||||
from esphome.cpp_types import ( # noqa
|
||||
global_ns,
|
||||
void,
|
||||
nullptr,
|
||||
float_,
|
||||
double,
|
||||
from esphome.cpp_types import ( # noqa: F401
|
||||
NAN,
|
||||
App,
|
||||
Application,
|
||||
Component,
|
||||
ComponentPtr,
|
||||
Controller,
|
||||
EntityBase,
|
||||
EntityCategory,
|
||||
ESPTime,
|
||||
GPIOPin,
|
||||
InternalGPIOPin,
|
||||
JsonObject,
|
||||
JsonObjectConst,
|
||||
Parented,
|
||||
PollingComponent,
|
||||
arduino_json_ns,
|
||||
bool_,
|
||||
const_char_ptr,
|
||||
double,
|
||||
esphome_ns,
|
||||
float_,
|
||||
global_ns,
|
||||
gpio_Flags,
|
||||
int16,
|
||||
int32,
|
||||
int64,
|
||||
int_,
|
||||
nullptr,
|
||||
optional,
|
||||
size_t,
|
||||
std_ns,
|
||||
std_shared_ptr,
|
||||
std_string,
|
||||
|
@ -66,28 +89,5 @@ from esphome.cpp_types import ( # noqa
|
|||
uint16,
|
||||
uint32,
|
||||
uint64,
|
||||
int16,
|
||||
int32,
|
||||
int64,
|
||||
size_t,
|
||||
const_char_ptr,
|
||||
NAN,
|
||||
esphome_ns,
|
||||
App,
|
||||
EntityBase,
|
||||
Component,
|
||||
ComponentPtr,
|
||||
PollingComponent,
|
||||
Application,
|
||||
optional,
|
||||
arduino_json_ns,
|
||||
JsonObject,
|
||||
JsonObjectConst,
|
||||
Controller,
|
||||
GPIOPin,
|
||||
InternalGPIOPin,
|
||||
gpio_Flags,
|
||||
EntityCategory,
|
||||
Parented,
|
||||
ESPTime,
|
||||
void,
|
||||
)
|
||||
|
|
|
@ -60,7 +60,7 @@ bool AdE7953Spi::ade_read_16(uint16_t reg, uint16_t *value) {
|
|||
this->write_byte16(reg);
|
||||
this->transfer_byte(0x80);
|
||||
uint8_t recv[2];
|
||||
this->read_array(recv, 4);
|
||||
this->read_array(recv, 2);
|
||||
*value = encode_uint16(recv[0], recv[1]);
|
||||
this->disable();
|
||||
return false;
|
||||
|
|
|
@ -1,25 +1,27 @@
|
|||
import base64
|
||||
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
from esphome.automation import Condition
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ACTION,
|
||||
CONF_ACTIONS,
|
||||
CONF_DATA,
|
||||
CONF_DATA_TEMPLATE,
|
||||
CONF_EVENT,
|
||||
CONF_ID,
|
||||
CONF_KEY,
|
||||
CONF_ON_CLIENT_CONNECTED,
|
||||
CONF_ON_CLIENT_DISCONNECTED,
|
||||
CONF_PASSWORD,
|
||||
CONF_PORT,
|
||||
CONF_REBOOT_TIMEOUT,
|
||||
CONF_SERVICE,
|
||||
CONF_VARIABLES,
|
||||
CONF_SERVICES,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_EVENT,
|
||||
CONF_TAG,
|
||||
CONF_ON_CLIENT_CONNECTED,
|
||||
CONF_ON_CLIENT_DISCONNECTED,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_VARIABLES,
|
||||
)
|
||||
from esphome.core import coroutine_with_priority
|
||||
|
||||
|
@ -63,7 +65,25 @@ def validate_encryption_key(value):
|
|||
return value
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
ACTIONS_SCHEMA = automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UserServiceTrigger),
|
||||
cv.Exclusive(CONF_SERVICE, group_of_exclusion=CONF_ACTION): cv.valid_name,
|
||||
cv.Exclusive(CONF_ACTION, group_of_exclusion=CONF_ACTION): cv.valid_name,
|
||||
cv.Optional(CONF_VARIABLES, default={}): cv.Schema(
|
||||
{
|
||||
cv.validate_id_name: cv.one_of(*SERVICE_ARG_NATIVE_TYPES, lower=True),
|
||||
}
|
||||
),
|
||||
},
|
||||
cv.All(
|
||||
cv.has_exactly_one_key(CONF_SERVICE, CONF_ACTION),
|
||||
cv.rename_key(CONF_SERVICE, CONF_ACTION),
|
||||
),
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(APIServer),
|
||||
cv.Optional(CONF_PORT, default=6053): cv.port,
|
||||
|
@ -71,19 +91,10 @@ CONFIG_SCHEMA = cv.Schema(
|
|||
cv.Optional(
|
||||
CONF_REBOOT_TIMEOUT, default="15min"
|
||||
): cv.positive_time_period_milliseconds,
|
||||
cv.Optional(CONF_SERVICES): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UserServiceTrigger),
|
||||
cv.Required(CONF_SERVICE): cv.valid_name,
|
||||
cv.Optional(CONF_VARIABLES, default={}): cv.Schema(
|
||||
{
|
||||
cv.validate_id_name: cv.one_of(
|
||||
*SERVICE_ARG_NATIVE_TYPES, lower=True
|
||||
),
|
||||
}
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Exclusive(
|
||||
CONF_SERVICES, group_of_exclusion=CONF_ACTIONS
|
||||
): ACTIONS_SCHEMA,
|
||||
cv.Exclusive(CONF_ACTIONS, group_of_exclusion=CONF_ACTIONS): ACTIONS_SCHEMA,
|
||||
cv.Optional(CONF_ENCRYPTION): cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_KEY): validate_encryption_key,
|
||||
|
@ -96,7 +107,9 @@ CONFIG_SCHEMA = cv.Schema(
|
|||
single=True
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
cv.rename_key(CONF_SERVICES, CONF_ACTIONS),
|
||||
)
|
||||
|
||||
|
||||
@coroutine_with_priority(40.0)
|
||||
|
@ -108,7 +121,7 @@ async def to_code(config):
|
|||
cg.add(var.set_password(config[CONF_PASSWORD]))
|
||||
cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
|
||||
|
||||
for conf in config.get(CONF_SERVICES, []):
|
||||
for conf in config.get(CONF_ACTIONS, []):
|
||||
template_args = []
|
||||
func_args = []
|
||||
service_arg_names = []
|
||||
|
@ -119,7 +132,7 @@ async def to_code(config):
|
|||
service_arg_names.append(name)
|
||||
templ = cg.TemplateArguments(*template_args)
|
||||
trigger = cg.new_Pvariable(
|
||||
conf[CONF_TRIGGER_ID], templ, conf[CONF_SERVICE], service_arg_names
|
||||
conf[CONF_TRIGGER_ID], templ, conf[CONF_ACTION], service_arg_names
|
||||
)
|
||||
cg.add(var.register_user_service(trigger))
|
||||
await automation.build_automation(trigger, func_args, conf)
|
||||
|
@ -152,28 +165,43 @@ async def to_code(config):
|
|||
|
||||
KEY_VALUE_SCHEMA = cv.Schema({cv.string: cv.templatable(cv.string_strict)})
|
||||
|
||||
HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema(
|
||||
|
||||
HOMEASSISTANT_ACTION_ACTION_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(APIServer),
|
||||
cv.Required(CONF_SERVICE): cv.templatable(cv.string),
|
||||
cv.Exclusive(CONF_SERVICE, group_of_exclusion=CONF_ACTION): cv.templatable(
|
||||
cv.string
|
||||
),
|
||||
cv.Exclusive(CONF_ACTION, group_of_exclusion=CONF_ACTION): cv.templatable(
|
||||
cv.string
|
||||
),
|
||||
cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA,
|
||||
cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA,
|
||||
cv.Optional(CONF_VARIABLES, default={}): cv.Schema(
|
||||
{cv.string: cv.returning_lambda}
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.has_exactly_one_key(CONF_SERVICE, CONF_ACTION),
|
||||
cv.rename_key(CONF_SERVICE, CONF_ACTION),
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"homeassistant.action",
|
||||
HomeAssistantServiceCallAction,
|
||||
HOMEASSISTANT_ACTION_ACTION_SCHEMA,
|
||||
)
|
||||
@automation.register_action(
|
||||
"homeassistant.service",
|
||||
HomeAssistantServiceCallAction,
|
||||
HOMEASSISTANT_SERVICE_ACTION_SCHEMA,
|
||||
HOMEASSISTANT_ACTION_ACTION_SCHEMA,
|
||||
)
|
||||
async def homeassistant_service_to_code(config, action_id, template_arg, args):
|
||||
serv = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, serv, False)
|
||||
templ = await cg.templatable(config[CONF_SERVICE], args, None)
|
||||
templ = await cg.templatable(config[CONF_ACTION], args, None)
|
||||
cg.add(var.set_service(templ))
|
||||
for key, value in config[CONF_DATA].items():
|
||||
templ = await cg.templatable(value, args, None)
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
from esphome.automation import maybe_simple_id
|
||||
from esphome.components import esp32_ble_tracker, esp32_ble_client
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import esp32_ble_client, esp32_ble_tracker
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_CHARACTERISTIC_UUID,
|
||||
CONF_ID,
|
||||
|
@ -13,7 +14,6 @@ from esphome.const import (
|
|||
CONF_TRIGGER_ID,
|
||||
CONF_VALUE,
|
||||
)
|
||||
from esphome import automation
|
||||
|
||||
AUTO_LOAD = ["esp32_ble_client"]
|
||||
CODEOWNERS = ["@buxtronix", "@clydebarrow"]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import ble_client, esp32_ble_tracker, output
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_CHARACTERISTIC_UUID, CONF_ID, CONF_SERVICE_UUID
|
||||
|
||||
from .. import ble_client_ns
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import ble_client, esp32_ble_tracker, sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, ble_client, esp32_ble_tracker
|
||||
from esphome.const import (
|
||||
CONF_CHARACTERISTIC_UUID,
|
||||
CONF_LAMBDA,
|
||||
CONF_SERVICE_UUID,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_TYPE,
|
||||
CONF_SERVICE_UUID,
|
||||
DEVICE_CLASS_SIGNAL_STRENGTH,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_DECIBEL_MILLIWATT,
|
||||
)
|
||||
from esphome import automation
|
||||
|
||||
from .. import ble_client_ns
|
||||
|
||||
DEPENDENCIES = ["ble_client"]
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import esphome.codegen as cg
|
||||
from esphome.components import ble_client, switch
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import switch, ble_client
|
||||
from esphome.const import ICON_BLUETOOTH
|
||||
|
||||
from .. import ble_client_ns
|
||||
|
||||
BLEClientSwitch = ble_client_ns.class_(
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import ble_client, esp32_ble_tracker, text_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import text_sensor, ble_client, esp32_ble_tracker
|
||||
from esphome.const import (
|
||||
CONF_CHARACTERISTIC_UUID,
|
||||
CONF_ID,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_SERVICE_UUID,
|
||||
CONF_TRIGGER_ID,
|
||||
)
|
||||
from esphome import automation
|
||||
|
||||
from .. import ble_client_ns
|
||||
|
||||
DEPENDENCIES = ["ble_client"]
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor, esp32_ble_tracker
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_MAC_ADDRESS,
|
||||
CONF_SERVICE_UUID,
|
||||
CONF_IBEACON_MAJOR,
|
||||
CONF_IBEACON_MINOR,
|
||||
CONF_IBEACON_UUID,
|
||||
CONF_MAC_ADDRESS,
|
||||
CONF_MIN_RSSI,
|
||||
CONF_SERVICE_UUID,
|
||||
CONF_TIMEOUT,
|
||||
)
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import esphome.codegen as cg
|
||||
from esphome.components import esp32_ble_tracker, sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, esp32_ble_tracker
|
||||
from esphome.const import (
|
||||
CONF_IBEACON_MAJOR,
|
||||
CONF_IBEACON_MINOR,
|
||||
CONF_IBEACON_UUID,
|
||||
CONF_SERVICE_UUID,
|
||||
CONF_MAC_ADDRESS,
|
||||
CONF_SERVICE_UUID,
|
||||
DEVICE_CLASS_SIGNAL_STRENGTH,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_DECIBEL_MILLIWATT,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import esphome.codegen as cg
|
||||
from esphome.components import esp32_ble_tracker, text_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import text_sensor, esp32_ble_tracker
|
||||
|
||||
DEPENDENCIES = ["esp32_ble_tracker"]
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from esphome.components import esp32_ble_tracker, esp32_ble_client
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
from esphome.const import CONF_ACTIVE, CONF_ID
|
||||
from esphome.components import esp32_ble_client, esp32_ble_tracker
|
||||
from esphome.components.esp32 import add_idf_sdkconfig_option
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ACTIVE, CONF_ID
|
||||
|
||||
AUTO_LOAD = ["esp32_ble_client", "esp32_ble_tracker"]
|
||||
DEPENDENCIES = ["api", "esp32"]
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import Union, Optional
|
||||
from pathlib import Path
|
||||
import logging
|
||||
import os
|
||||
import esphome.final_validate as fv
|
||||
from pathlib import Path
|
||||
from typing import Optional, Union
|
||||
|
||||
from esphome.helpers import copy_file_if_changed, write_file_if_changed, mkdir_p
|
||||
from esphome import git
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ADVANCED,
|
||||
CONF_BOARD,
|
||||
|
@ -15,6 +16,7 @@ from esphome.const import (
|
|||
CONF_IGNORE_EFUSE_MAC_CRC,
|
||||
CONF_NAME,
|
||||
CONF_PATH,
|
||||
CONF_PLATFORM_VERSION,
|
||||
CONF_PLATFORMIO_OPTIONS,
|
||||
CONF_REF,
|
||||
CONF_REFRESH,
|
||||
|
@ -32,13 +34,12 @@ from esphome.const import (
|
|||
TYPE_GIT,
|
||||
TYPE_LOCAL,
|
||||
__version__,
|
||||
CONF_PLATFORM_VERSION,
|
||||
)
|
||||
from esphome.core import CORE, HexInt, TimePeriod
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
from esphome import git
|
||||
import esphome.final_validate as fv
|
||||
from esphome.helpers import copy_file_if_changed, mkdir_p, write_file_if_changed
|
||||
|
||||
from .boards import BOARDS
|
||||
from .const import ( # noqa
|
||||
KEY_BOARD,
|
||||
KEY_COMPONENTS,
|
||||
|
@ -54,12 +55,10 @@ from .const import ( # noqa
|
|||
VARIANT_FRIENDLY,
|
||||
VARIANTS,
|
||||
)
|
||||
from .boards import BOARDS
|
||||
|
||||
# force import gpio to register pin schema
|
||||
from .gpio import esp32_pin_to_code # noqa
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
CODEOWNERS = ["@esphome/core"]
|
||||
AUTO_LOAD = ["preferences"]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from .const import VARIANT_ESP32, VARIANT_ESP32S2, VARIANT_ESP32C3, VARIANT_ESP32S3
|
||||
from .const import VARIANT_ESP32, VARIANT_ESP32C3, VARIANT_ESP32S2, VARIANT_ESP32S3
|
||||
|
||||
ESP32_BASE_PINS = {
|
||||
"TX": 1,
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from esphome import pins
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_IGNORE_PIN_VALIDATION_ERROR,
|
||||
CONF_IGNORE_STRAPPING_WARNING,
|
||||
CONF_INVERTED,
|
||||
CONF_MODE,
|
||||
CONF_NUMBER,
|
||||
CONF_OPEN_DRAIN,
|
||||
CONF_OUTPUT,
|
||||
CONF_IGNORE_PIN_VALIDATION_ERROR,
|
||||
CONF_IGNORE_STRAPPING_WARNING,
|
||||
PLATFORM_ESP32,
|
||||
)
|
||||
from esphome import pins
|
||||
from esphome.core import CORE
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
|
||||
from . import boards
|
||||
from .const import (
|
||||
|
@ -24,22 +24,21 @@ from .const import (
|
|||
KEY_ESP32,
|
||||
KEY_VARIANT,
|
||||
VARIANT_ESP32,
|
||||
VARIANT_ESP32C3,
|
||||
VARIANT_ESP32S2,
|
||||
VARIANT_ESP32S3,
|
||||
VARIANT_ESP32C2,
|
||||
VARIANT_ESP32C3,
|
||||
VARIANT_ESP32C6,
|
||||
VARIANT_ESP32H2,
|
||||
VARIANT_ESP32S2,
|
||||
VARIANT_ESP32S3,
|
||||
esp32_ns,
|
||||
)
|
||||
|
||||
from .gpio_esp32 import esp32_validate_gpio_pin, esp32_validate_supports
|
||||
from .gpio_esp32_s2 import esp32_s2_validate_gpio_pin, esp32_s2_validate_supports
|
||||
from .gpio_esp32_c3 import esp32_c3_validate_gpio_pin, esp32_c3_validate_supports
|
||||
from .gpio_esp32_s3 import esp32_s3_validate_gpio_pin, esp32_s3_validate_supports
|
||||
from .gpio_esp32_c2 import esp32_c2_validate_gpio_pin, esp32_c2_validate_supports
|
||||
from .gpio_esp32_c3 import esp32_c3_validate_gpio_pin, esp32_c3_validate_supports
|
||||
from .gpio_esp32_c6 import esp32_c6_validate_gpio_pin, esp32_c6_validate_supports
|
||||
from .gpio_esp32_h2 import esp32_h2_validate_gpio_pin, esp32_h2_validate_supports
|
||||
from .gpio_esp32_s2 import esp32_s2_validate_gpio_pin, esp32_s2_validate_supports
|
||||
from .gpio_esp32_s3 import esp32_s3_validate_gpio_pin, esp32_s3_validate_supports
|
||||
|
||||
ESP32InternalGPIOPin = esp32_ns.class_("ESP32InternalGPIOPin", cg.InternalGPIOPin)
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import logging
|
||||
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_INPUT,
|
||||
CONF_MODE,
|
||||
|
@ -8,10 +9,8 @@ from esphome.const import (
|
|||
CONF_PULLDOWN,
|
||||
CONF_PULLUP,
|
||||
)
|
||||
import esphome.config_validation as cv
|
||||
from esphome.pins import check_strapping_pin
|
||||
|
||||
|
||||
_ESP_SDIO_PINS = {
|
||||
6: "Flash Clock",
|
||||
7: "Flash Data 0",
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import logging
|
||||
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER
|
||||
from esphome.pins import check_strapping_pin
|
||||
|
||||
import esphome.config_validation as cv
|
||||
|
||||
_ESP32C2_STRAPPING_PINS = {8, 9}
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
import logging
|
||||
|
||||
from esphome.const import (
|
||||
CONF_INPUT,
|
||||
CONF_MODE,
|
||||
CONF_NUMBER,
|
||||
)
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER
|
||||
from esphome.pins import check_strapping_pin
|
||||
|
||||
_ESP32C3_SPI_PSRAM_PINS = {
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import logging
|
||||
|
||||
from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER
|
||||
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER
|
||||
from esphome.pins import check_strapping_pin
|
||||
|
||||
_ESP32C6_SPI_PSRAM_PINS = {
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import logging
|
||||
|
||||
from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER
|
||||
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER
|
||||
|
||||
_ESP32H2_SPI_FLASH_PINS = {6, 7, 15, 16, 17, 18, 19, 20, 21}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import logging
|
||||
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_INPUT,
|
||||
CONF_MODE,
|
||||
|
@ -8,8 +9,6 @@ from esphome.const import (
|
|||
CONF_PULLDOWN,
|
||||
CONF_PULLUP,
|
||||
)
|
||||
|
||||
import esphome.config_validation as cv
|
||||
from esphome.pins import check_strapping_pin
|
||||
|
||||
_ESP32S2_SPI_PSRAM_PINS = {
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
import logging
|
||||
|
||||
from esphome.const import (
|
||||
CONF_INPUT,
|
||||
CONF_MODE,
|
||||
CONF_NUMBER,
|
||||
)
|
||||
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER
|
||||
from esphome.pins import check_strapping_pin
|
||||
|
||||
_ESP_32S3_SPI_PSRAM_PINS = {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.esp32 import add_idf_sdkconfig_option, const, get_esp32_variant
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ENABLE_ON_BOOT, CONF_ID
|
||||
from esphome.core import CORE
|
||||
from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant, const
|
||||
|
||||
DEPENDENCIES = ["esp32"]
|
||||
CODEOWNERS = ["@jesserockz", "@Rapsssito"]
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components.esp32_ble import CONF_BLE_ID
|
||||
from esphome.const import CONF_ID, CONF_TYPE, CONF_UUID, CONF_TX_POWER
|
||||
from esphome.core import CORE, TimePeriod
|
||||
from esphome.components.esp32 import add_idf_sdkconfig_option
|
||||
from esphome.components import esp32_ble
|
||||
from esphome.components.esp32 import add_idf_sdkconfig_option
|
||||
from esphome.components.esp32_ble import CONF_BLE_ID
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_TX_POWER, CONF_TYPE, CONF_UUID
|
||||
from esphome.core import CORE, TimePeriod
|
||||
|
||||
AUTO_LOAD = ["esp32_ble"]
|
||||
DEPENDENCIES = ["esp32"]
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import esphome.codegen as cg
|
||||
|
||||
from esphome.components import esp32_ble_tracker
|
||||
|
||||
AUTO_LOAD = ["esp32_ble_tracker"]
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import esphome.codegen as cg
|
||||
from esphome.components import esp32_ble
|
||||
from esphome.components.esp32 import add_idf_sdkconfig_option
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_MODEL
|
||||
from esphome.components import esp32_ble
|
||||
from esphome.core import CORE
|
||||
from esphome.components.esp32 import add_idf_sdkconfig_option
|
||||
|
||||
AUTO_LOAD = ["esp32_ble"]
|
||||
CODEOWNERS = ["@jesserockz", "@clydebarrow", "@Rapsssito"]
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import re
|
||||
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import esp32_ble
|
||||
from esphome.components.esp32 import add_idf_sdkconfig_option
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ACTIVE,
|
||||
CONF_DURATION,
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
from esphome import pins
|
||||
import esphome.config_validation as cv
|
||||
import esphome.final_validate as fv
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant
|
||||
from esphome.components.esp32.const import (
|
||||
|
@ -8,31 +6,33 @@ from esphome.components.esp32.const import (
|
|||
VARIANT_ESP32S2,
|
||||
VARIANT_ESP32S3,
|
||||
)
|
||||
from esphome.components.network import IPAddress
|
||||
from esphome.components.spi import CONF_INTERFACE_INDEX, get_spi_interface
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_DOMAIN,
|
||||
CONF_ID,
|
||||
CONF_VALUE,
|
||||
CONF_MANUAL_IP,
|
||||
CONF_STATIC_IP,
|
||||
CONF_TYPE,
|
||||
CONF_USE_ADDRESS,
|
||||
CONF_GATEWAY,
|
||||
CONF_SUBNET,
|
||||
CONF_ADDRESS,
|
||||
CONF_CLK_PIN,
|
||||
CONF_CS_PIN,
|
||||
CONF_DNS1,
|
||||
CONF_DNS2,
|
||||
CONF_CLK_PIN,
|
||||
CONF_DOMAIN,
|
||||
CONF_GATEWAY,
|
||||
CONF_ID,
|
||||
CONF_INTERRUPT_PIN,
|
||||
CONF_MANUAL_IP,
|
||||
CONF_MISO_PIN,
|
||||
CONF_MOSI_PIN,
|
||||
CONF_CS_PIN,
|
||||
CONF_INTERRUPT_PIN,
|
||||
CONF_PAGE_ID,
|
||||
CONF_RESET_PIN,
|
||||
CONF_SPI,
|
||||
CONF_PAGE_ID,
|
||||
CONF_ADDRESS,
|
||||
CONF_STATIC_IP,
|
||||
CONF_SUBNET,
|
||||
CONF_TYPE,
|
||||
CONF_USE_ADDRESS,
|
||||
CONF_VALUE,
|
||||
)
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
from esphome.components.network import IPAddress
|
||||
from esphome.components.spi import get_spi_interface, CONF_INTERFACE_INDEX
|
||||
import esphome.final_validate as fv
|
||||
|
||||
CONFLICTS_WITH = ["wifi"]
|
||||
DEPENDENCIES = ["esp32"]
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import text_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_IP_ADDRESS,
|
||||
CONF_DNS_ADDRESS,
|
||||
CONF_IP_ADDRESS,
|
||||
CONF_MAC_ADDRESS,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
)
|
||||
|
|
|
@ -14,7 +14,7 @@ from esphome.const import (
|
|||
from esphome.core import CORE, Lambda
|
||||
|
||||
DEPENDENCIES = ["network"]
|
||||
AUTO_LOAD = ["json"]
|
||||
AUTO_LOAD = ["json", "watchdog"]
|
||||
|
||||
http_request_ns = cg.esphome_ns.namespace("http_request")
|
||||
HttpRequestComponent = http_request_ns.class_("HttpRequestComponent", cg.Component)
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/components/network/util.h"
|
||||
#include "esphome/components/watchdog/watchdog.h"
|
||||
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#include "watchdog.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace http_request {
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#ifdef USE_ESP_IDF
|
||||
|
||||
#include "esphome/components/network/util.h"
|
||||
#include "esphome/components/watchdog/watchdog.h"
|
||||
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
@ -11,8 +13,6 @@
|
|||
#include "esp_crt_bundle.h"
|
||||
#endif
|
||||
|
||||
#include "watchdog.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace http_request {
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
#include "ota_http_request.h"
|
||||
#include "../watchdog.h"
|
||||
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#include "esphome/components/md5/md5.h"
|
||||
#include "esphome/components/watchdog/watchdog.h"
|
||||
#include "esphome/components/ota/ota_backend.h"
|
||||
#include "esphome/components/ota/ota_backend_arduino_esp32.h"
|
||||
#include "esphome/components/ota/ota_backend_arduino_esp8266.h"
|
||||
|
|
|
@ -138,8 +138,8 @@ void HttpRequestUpdate::update() {
|
|||
this->publish_state();
|
||||
}
|
||||
|
||||
void HttpRequestUpdate::perform() {
|
||||
if (this->state_ != update::UPDATE_STATE_AVAILABLE) {
|
||||
void HttpRequestUpdate::perform(bool force) {
|
||||
if (this->state_ != update::UPDATE_STATE_AVAILABLE && !force) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ class HttpRequestUpdate : public update::UpdateEntity, public PollingComponent {
|
|||
void setup() override;
|
||||
void update() override;
|
||||
|
||||
void perform() override;
|
||||
void perform(bool force) override;
|
||||
|
||||
void set_source_url(const std::string &source_url) { this->source_url_ = source_url; }
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import re
|
||||
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import __version__
|
||||
|
||||
CODEOWNERS = ["@esphome/core"]
|
||||
|
@ -39,4 +38,4 @@ def _process_next_url(url: str):
|
|||
async def setup_improv_core(var, config):
|
||||
if CONF_NEXT_URL in config:
|
||||
cg.add(var.set_next_url(_process_next_url(config[CONF_NEXT_URL])))
|
||||
cg.add_library("esphome/Improv", "1.2.3")
|
||||
cg.add_library("improv/Improv", "1.2.4")
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import logging
|
||||
|
||||
from esphome.automation import build_automation, register_action, validate_automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.display import Display
|
||||
import esphome.config_validation as cv
|
||||
|
@ -8,7 +9,11 @@ from esphome.const import (
|
|||
CONF_BUFFER_SIZE,
|
||||
CONF_ID,
|
||||
CONF_LAMBDA,
|
||||
CONF_ON_IDLE,
|
||||
CONF_PAGES,
|
||||
CONF_TIMEOUT,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_TYPE,
|
||||
)
|
||||
from esphome.core import CORE, ID, Lambda
|
||||
from esphome.cpp_generator import MockObj
|
||||
|
@ -16,14 +21,26 @@ from esphome.final_validate import full_config
|
|||
from esphome.helpers import write_file_if_changed
|
||||
|
||||
from . import defines as df, helpers, lv_validation as lvalid
|
||||
from .automation import update_to_code
|
||||
from .btn import btn_spec
|
||||
from .label import label_spec
|
||||
from .lvcode import ConstantLiteral, LvContext
|
||||
|
||||
# from .menu import menu_spec
|
||||
from .lv_validation import lv_images_used
|
||||
from .lvcode import LvContext
|
||||
from .obj import obj_spec
|
||||
from .schemas import WIDGET_TYPES, any_widget_schema, obj_schema
|
||||
from .types import FontEngine, LvglComponent, lv_disp_t_ptr, lv_font_t, lvgl_ns
|
||||
from .widget import LvScrActType, Widget, add_widgets, set_obj_properties
|
||||
from .rotary_encoders import ROTARY_ENCODER_CONFIG, rotary_encoders_to_code
|
||||
from .schemas import any_widget_schema, create_modify_schema, obj_schema
|
||||
from .touchscreens import touchscreen_schema, touchscreens_to_code
|
||||
from .trigger import generate_triggers
|
||||
from .types import (
|
||||
WIDGET_TYPES,
|
||||
FontEngine,
|
||||
IdleTrigger,
|
||||
LvglComponent,
|
||||
ObjUpdateAction,
|
||||
lv_font_t,
|
||||
lvgl_ns,
|
||||
)
|
||||
from .widget import Widget, add_widgets, lv_scr_act, set_obj_properties
|
||||
|
||||
DOMAIN = "lvgl"
|
||||
DEPENDENCIES = ("display",)
|
||||
|
@ -31,23 +48,24 @@ AUTO_LOAD = ("key_provider",)
|
|||
CODEOWNERS = ("@clydebarrow",)
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
for widg in (
|
||||
label_spec,
|
||||
obj_spec,
|
||||
):
|
||||
WIDGET_TYPES[widg.name] = widg
|
||||
|
||||
lv_scr_act_spec = LvScrActType()
|
||||
lv_scr_act = Widget.create(
|
||||
None, ConstantLiteral("lv_scr_act()"), lv_scr_act_spec, {}, parent=None
|
||||
)
|
||||
for w_type in (label_spec, obj_spec, btn_spec):
|
||||
WIDGET_TYPES[w_type.name] = w_type
|
||||
|
||||
WIDGET_SCHEMA = any_widget_schema()
|
||||
|
||||
for w_type in WIDGET_TYPES.values():
|
||||
register_action(
|
||||
f"lvgl.{w_type.name}.update",
|
||||
ObjUpdateAction,
|
||||
create_modify_schema(w_type),
|
||||
)(update_to_code)
|
||||
|
||||
|
||||
async def add_init_lambda(lv_component, init):
|
||||
if init:
|
||||
lamb = await cg.process_lambda(Lambda(init), [(lv_disp_t_ptr, "lv_disp")])
|
||||
lamb = await cg.process_lambda(
|
||||
Lambda(init), [(LvglComponent.operator("ptr"), "lv_component")]
|
||||
)
|
||||
cg.add(lv_component.add_init_lambda(lamb))
|
||||
|
||||
|
||||
|
@ -93,8 +111,15 @@ def final_validation(config):
|
|||
"Using auto_clear_enabled: true in display config not compatible with LVGL"
|
||||
)
|
||||
buffer_frac = config[CONF_BUFFER_SIZE]
|
||||
if not CORE.is_host and buffer_frac > 0.5 and "psram" not in global_config:
|
||||
if CORE.is_esp32 and buffer_frac > 0.5 and "psram" not in global_config:
|
||||
LOGGER.warning("buffer_size: may need to be reduced without PSRAM")
|
||||
for image_id in lv_images_used:
|
||||
path = global_config.get_path_for_id(image_id)[:-1]
|
||||
image_conf = global_config.get_config_for_path(path)
|
||||
if image_conf[CONF_TYPE] in ("RGBA", "RGB24"):
|
||||
raise cv.Invalid(
|
||||
"Using RGBA or RGB24 in image config not compatible with LVGL", path
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
|
@ -132,7 +157,7 @@ async def to_code(config):
|
|||
cg.add_global(lvgl_ns.using)
|
||||
lv_component = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(lv_component, config)
|
||||
Widget.create(config[CONF_ID], lv_component, WIDGET_TYPES[df.CONF_OBJ], config)
|
||||
Widget.create(config[CONF_ID], lv_component, obj_spec, config)
|
||||
for display in config[df.CONF_DISPLAYS]:
|
||||
cg.add(lv_component.add_display(await cg.get_variable(display)))
|
||||
|
||||
|
@ -152,7 +177,7 @@ async def to_code(config):
|
|||
await cg.get_variable(font)
|
||||
cg.new_Pvariable(ID(f"{font}_engine", True, type=FontEngine), MockObj(font))
|
||||
default_font = config[df.CONF_DEFAULT_FONT]
|
||||
if default_font not in helpers.lv_fonts_used:
|
||||
if not lvalid.is_lv_font(default_font):
|
||||
add_define(
|
||||
"LV_FONT_CUSTOM_DECLARE", f"LV_FONT_DECLARE(*{df.DEFAULT_ESPHOME_FONT})"
|
||||
)
|
||||
|
@ -161,15 +186,24 @@ async def to_code(config):
|
|||
True,
|
||||
type=lv_font_t.operator("ptr").operator("const"),
|
||||
)
|
||||
cg.new_variable(globfont_id, MockObj(default_font))
|
||||
cg.new_variable(
|
||||
globfont_id, MockObj(await lvalid.lv_font.process(default_font))
|
||||
)
|
||||
add_define("LV_FONT_DEFAULT", df.DEFAULT_ESPHOME_FONT)
|
||||
else:
|
||||
add_define("LV_FONT_DEFAULT", default_font)
|
||||
add_define("LV_FONT_DEFAULT", await lvalid.lv_font.process(default_font))
|
||||
|
||||
with LvContext():
|
||||
await touchscreens_to_code(lv_component, config)
|
||||
await rotary_encoders_to_code(lv_component, config)
|
||||
await set_obj_properties(lv_scr_act, config)
|
||||
await add_widgets(lv_scr_act, config)
|
||||
Widget.set_completed()
|
||||
await generate_triggers(lv_component)
|
||||
for conf in config.get(CONF_ON_IDLE, ()):
|
||||
templ = await cg.templatable(conf[CONF_TIMEOUT], [], cg.uint32)
|
||||
idle_trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], lv_component, templ)
|
||||
await build_automation(idle_trigger, [], conf)
|
||||
await add_init_lambda(lv_component, LvContext.get_code())
|
||||
for comp in helpers.lvgl_components_required:
|
||||
CORE.add_define(f"USE_LVGL_{comp.upper()}")
|
||||
|
@ -190,7 +224,7 @@ FINAL_VALIDATE_SCHEMA = final_validation
|
|||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.polling_component_schema("1s")
|
||||
.extend(obj_schema("obj"))
|
||||
.extend(obj_schema(obj_spec))
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_ID): cv.declare_id(LvglComponent),
|
||||
|
@ -205,8 +239,18 @@ CONFIG_SCHEMA = (
|
|||
cv.Optional(df.CONF_BYTE_ORDER, default="big_endian"): cv.one_of(
|
||||
"big_endian", "little_endian"
|
||||
),
|
||||
cv.Optional(CONF_ON_IDLE): validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(IdleTrigger),
|
||||
cv.Required(CONF_TIMEOUT): cv.templatable(
|
||||
cv.positive_time_period_milliseconds
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(df.CONF_WIDGETS): cv.ensure_list(WIDGET_SCHEMA),
|
||||
cv.Optional(df.CONF_TRANSPARENCY_KEY, default=0x000400): lvalid.lv_color,
|
||||
cv.GenerateID(df.CONF_TOUCHSCREENS): touchscreen_schema,
|
||||
cv.GenerateID(df.CONF_ROTARY_ENCODERS): ROTARY_ENCODER_CONFIG,
|
||||
}
|
||||
)
|
||||
).add_extra(cv.has_at_least_one_key(CONF_PAGES, df.CONF_WIDGETS))
|
||||
|
|
188
esphome/components/lvgl/automation.py
Normal file
188
esphome/components/lvgl/automation.py
Normal file
|
@ -0,0 +1,188 @@
|
|||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_TIMEOUT
|
||||
from esphome.core import Lambda
|
||||
from esphome.cpp_generator import RawStatement
|
||||
from esphome.cpp_types import nullptr
|
||||
|
||||
from .defines import CONF_LVGL_ID, CONF_SHOW_SNOW, literal
|
||||
from .lv_validation import lv_bool
|
||||
from .lvcode import (
|
||||
LambdaContext,
|
||||
ReturnStatement,
|
||||
add_line_marks,
|
||||
lv,
|
||||
lv_add,
|
||||
lv_obj,
|
||||
lvgl_comp,
|
||||
)
|
||||
from .schemas import ACTION_SCHEMA, LVGL_SCHEMA
|
||||
from .types import (
|
||||
LvglAction,
|
||||
LvglComponent,
|
||||
LvglComponentPtr,
|
||||
LvglCondition,
|
||||
ObjUpdateAction,
|
||||
lv_obj_t,
|
||||
)
|
||||
from .widget import Widget, get_widget, lv_scr_act, set_obj_properties
|
||||
|
||||
|
||||
async def action_to_code(action: list, action_id, widget: Widget, template_arg, args):
|
||||
with LambdaContext() as context:
|
||||
lv.cond_if(widget.obj == nullptr)
|
||||
lv_add(RawStatement(" return;"))
|
||||
lv.cond_endif()
|
||||
code = context.get_code()
|
||||
code.extend(action)
|
||||
action = "\n".join(code) + "\n\n"
|
||||
lamb = await cg.process_lambda(Lambda(action), args)
|
||||
var = cg.new_Pvariable(action_id, template_arg, lamb)
|
||||
return var
|
||||
|
||||
|
||||
async def update_to_code(config, action_id, template_arg, args):
|
||||
if config is not None:
|
||||
widget = await get_widget(config)
|
||||
with LambdaContext() as context:
|
||||
add_line_marks(action_id)
|
||||
await set_obj_properties(widget, config)
|
||||
await widget.type.to_code(widget, config)
|
||||
if (
|
||||
widget.type.w_type.value_property is not None
|
||||
and widget.type.w_type.value_property in config
|
||||
):
|
||||
lv.event_send(widget.obj, literal("LV_EVENT_VALUE_CHANGED"), nullptr)
|
||||
return await action_to_code(
|
||||
context.get_code(), action_id, widget, template_arg, args
|
||||
)
|
||||
|
||||
|
||||
@automation.register_condition(
|
||||
"lvgl.is_paused",
|
||||
LvglCondition,
|
||||
LVGL_SCHEMA,
|
||||
)
|
||||
async def lvgl_is_paused(config, condition_id, template_arg, args):
|
||||
lvgl = config[CONF_LVGL_ID]
|
||||
with LambdaContext(
|
||||
[(LvglComponentPtr, "lvgl_comp")], return_type=cg.bool_
|
||||
) as context:
|
||||
lv_add(ReturnStatement(lvgl_comp.is_paused()))
|
||||
var = cg.new_Pvariable(condition_id, template_arg, await context.get_lambda())
|
||||
await cg.register_parented(var, lvgl)
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_condition(
|
||||
"lvgl.is_idle",
|
||||
LvglCondition,
|
||||
LVGL_SCHEMA.extend(
|
||||
{
|
||||
cv.Required(CONF_TIMEOUT): cv.templatable(
|
||||
cv.positive_time_period_milliseconds
|
||||
)
|
||||
}
|
||||
),
|
||||
)
|
||||
async def lvgl_is_idle(config, condition_id, template_arg, args):
|
||||
lvgl = config[CONF_LVGL_ID]
|
||||
timeout = await cg.templatable(config[CONF_TIMEOUT], [], cg.uint32)
|
||||
with LambdaContext(
|
||||
[(LvglComponentPtr, "lvgl_comp")], return_type=cg.bool_
|
||||
) as context:
|
||||
lv_add(ReturnStatement(lvgl_comp.is_idle(timeout)))
|
||||
var = cg.new_Pvariable(condition_id, template_arg, await context.get_lambda())
|
||||
await cg.register_parented(var, lvgl)
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"lvgl.widget.redraw",
|
||||
ObjUpdateAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_ID): cv.use_id(lv_obj_t),
|
||||
cv.GenerateID(CONF_LVGL_ID): cv.use_id(LvglComponent),
|
||||
}
|
||||
),
|
||||
)
|
||||
async def obj_invalidate_to_code(config, action_id, template_arg, args):
|
||||
if CONF_ID in config:
|
||||
w = await get_widget(config)
|
||||
else:
|
||||
w = lv_scr_act
|
||||
with LambdaContext() as context:
|
||||
add_line_marks(action_id)
|
||||
lv_obj.invalidate(w.obj)
|
||||
return await action_to_code(context.get_code(), action_id, w, template_arg, args)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"lvgl.pause",
|
||||
LvglAction,
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(LvglComponent),
|
||||
cv.Optional(CONF_SHOW_SNOW, default=False): lv_bool,
|
||||
},
|
||||
)
|
||||
async def pause_action_to_code(config, action_id, template_arg, args):
|
||||
with LambdaContext([(LvglComponentPtr, "lvgl_comp")]) as context:
|
||||
add_line_marks(action_id)
|
||||
lv_add(lvgl_comp.set_paused(True, config[CONF_SHOW_SNOW]))
|
||||
var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda())
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"lvgl.resume",
|
||||
LvglAction,
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(LvglComponent),
|
||||
},
|
||||
)
|
||||
async def resume_action_to_code(config, action_id, template_arg, args):
|
||||
with LambdaContext([(LvglComponentPtr, "lvgl_comp")]) as context:
|
||||
add_line_marks(action_id)
|
||||
lv_add(lvgl_comp.set_paused(False, False))
|
||||
var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda())
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action("lvgl.widget.disable", ObjUpdateAction, ACTION_SCHEMA)
|
||||
async def obj_disable_to_code(config, action_id, template_arg, args):
|
||||
w = await get_widget(config)
|
||||
with LambdaContext() as context:
|
||||
add_line_marks(action_id)
|
||||
w.add_state("LV_STATE_DISABLED")
|
||||
return await action_to_code(context.get_code(), action_id, w, template_arg, args)
|
||||
|
||||
|
||||
@automation.register_action("lvgl.widget.enable", ObjUpdateAction, ACTION_SCHEMA)
|
||||
async def obj_enable_to_code(config, action_id, template_arg, args):
|
||||
w = await get_widget(config)
|
||||
with LambdaContext() as context:
|
||||
add_line_marks(action_id)
|
||||
w.clear_state("LV_STATE_DISABLED")
|
||||
return await action_to_code(context.get_code(), action_id, w, template_arg, args)
|
||||
|
||||
|
||||
@automation.register_action("lvgl.widget.hide", ObjUpdateAction, ACTION_SCHEMA)
|
||||
async def obj_hide_to_code(config, action_id, template_arg, args):
|
||||
w = await get_widget(config)
|
||||
with LambdaContext() as context:
|
||||
add_line_marks(action_id)
|
||||
w.add_flag("LV_OBJ_FLAG_HIDDEN")
|
||||
return await action_to_code(context.get_code(), action_id, w, template_arg, args)
|
||||
|
||||
|
||||
@automation.register_action("lvgl.widget.show", ObjUpdateAction, ACTION_SCHEMA)
|
||||
async def obj_show_to_code(config, action_id, template_arg, args):
|
||||
w = await get_widget(config)
|
||||
with LambdaContext() as context:
|
||||
add_line_marks(action_id)
|
||||
w.clear_flag("LV_OBJ_FLAG_HIDDEN")
|
||||
return await action_to_code(context.get_code(), action_id, w, template_arg, args)
|
25
esphome/components/lvgl/btn.py
Normal file
25
esphome/components/lvgl/btn.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
from esphome.const import CONF_BUTTON
|
||||
from esphome.cpp_generator import MockObjClass
|
||||
|
||||
from .defines import CONF_MAIN
|
||||
from .types import LvBoolean, WidgetType
|
||||
|
||||
|
||||
class BtnType(WidgetType):
|
||||
def __init__(self):
|
||||
super().__init__(CONF_BUTTON, LvBoolean("lv_btn_t"), (CONF_MAIN,))
|
||||
|
||||
def obj_creator(self, parent: MockObjClass, config: dict):
|
||||
"""
|
||||
LVGL 8 calls buttons `btn`
|
||||
"""
|
||||
return f"lv_btn_create({parent})"
|
||||
|
||||
def get_uses(self):
|
||||
return ("btn",)
|
||||
|
||||
async def to_code(self, w, config):
|
||||
return []
|
||||
|
||||
|
||||
btn_spec = BtnType()
|
|
@ -4,12 +4,32 @@ Constants already defined in esphome.const are not duplicated here and must be i
|
|||
|
||||
"""
|
||||
|
||||
from typing import Union
|
||||
|
||||
from esphome import codegen as cg, config_validation as cv
|
||||
from esphome.core import ID, Lambda
|
||||
from esphome.cpp_generator import Literal
|
||||
from esphome.cpp_types import uint32
|
||||
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
|
||||
|
||||
from .lvcode import ConstantLiteral
|
||||
from .helpers import requires_component
|
||||
|
||||
|
||||
class ConstantLiteral(Literal):
|
||||
__slots__ = ("constant",)
|
||||
|
||||
def __init__(self, constant: str):
|
||||
super().__init__()
|
||||
self.constant = constant
|
||||
|
||||
def __str__(self):
|
||||
return self.constant
|
||||
|
||||
|
||||
def literal(arg: Union[str, ConstantLiteral]):
|
||||
if isinstance(arg, str):
|
||||
return ConstantLiteral(arg)
|
||||
return arg
|
||||
|
||||
|
||||
class LValidator:
|
||||
|
@ -18,14 +38,19 @@ class LValidator:
|
|||
has `process()` to convert a value during code generation
|
||||
"""
|
||||
|
||||
def __init__(self, validator, rtype, idtype=None, idexpr=None, retmapper=None):
|
||||
def __init__(
|
||||
self, validator, rtype, idtype=None, idexpr=None, retmapper=None, requires=None
|
||||
):
|
||||
self.validator = validator
|
||||
self.rtype = rtype
|
||||
self.idtype = idtype
|
||||
self.idexpr = idexpr
|
||||
self.retmapper = retmapper
|
||||
self.requires = requires
|
||||
|
||||
def __call__(self, value):
|
||||
if self.requires:
|
||||
value = requires_component(self.requires)(value)
|
||||
if isinstance(value, cv.Lambda):
|
||||
return cv.returning_lambda(value)
|
||||
if self.idtype is not None and isinstance(value, ID):
|
||||
|
@ -422,6 +447,7 @@ CONF_RECOLOR = "recolor"
|
|||
CONF_RIGHT_BUTTON = "right_button"
|
||||
CONF_ROLLOVER = "rollover"
|
||||
CONF_ROOT_BACK_BTN = "root_back_btn"
|
||||
CONF_ROTARY_ENCODERS = "rotary_encoders"
|
||||
CONF_ROWS = "rows"
|
||||
CONF_SCALES = "scales"
|
||||
CONF_SCALE_LINES = "scale_lines"
|
||||
|
@ -446,6 +472,7 @@ CONF_TILE_ID = "tile_id"
|
|||
CONF_TILES = "tiles"
|
||||
CONF_TITLE = "title"
|
||||
CONF_TOP_LAYER = "top_layer"
|
||||
CONF_TOUCHSCREENS = "touchscreens"
|
||||
CONF_TRANSPARENCY_KEY = "transparency_key"
|
||||
CONF_THEME = "theme"
|
||||
CONF_VISIBLE_ROW_COUNT = "visible_row_count"
|
||||
|
@ -474,14 +501,8 @@ LV_KEYS = LvConstant(
|
|||
)
|
||||
|
||||
|
||||
# list of widgets and the parts allowed
|
||||
WIDGET_PARTS = {
|
||||
CONF_LABEL: (CONF_MAIN, CONF_SCROLLBAR, CONF_SELECTED),
|
||||
CONF_OBJ: (CONF_MAIN,),
|
||||
}
|
||||
|
||||
DEFAULT_ESPHOME_FONT = "esphome_lv_default_font"
|
||||
|
||||
|
||||
def join_enums(enums, prefix=""):
|
||||
return "|".join(f"(int){prefix}{e.upper()}" for e in enums)
|
||||
return ConstantLiteral("|".join(f"(int){prefix}{e.upper()}" for e in enums))
|
||||
|
|
|
@ -22,7 +22,6 @@ def add_lv_use(*names):
|
|||
|
||||
lv_fonts_used = set()
|
||||
esphome_fonts_used = set()
|
||||
REQUIRED_COMPONENTS = {}
|
||||
lvgl_components_required = set()
|
||||
|
||||
|
||||
|
|
|
@ -1,16 +1,27 @@
|
|||
import esphome.config_validation as cv
|
||||
|
||||
from .defines import CONF_LABEL, CONF_LONG_MODE, CONF_RECOLOR, CONF_TEXT, LV_LONG_MODES
|
||||
from .defines import (
|
||||
CONF_LABEL,
|
||||
CONF_LONG_MODE,
|
||||
CONF_MAIN,
|
||||
CONF_RECOLOR,
|
||||
CONF_SCROLLBAR,
|
||||
CONF_SELECTED,
|
||||
CONF_TEXT,
|
||||
LV_LONG_MODES,
|
||||
)
|
||||
from .lv_validation import lv_bool, lv_text
|
||||
from .schemas import TEXT_SCHEMA
|
||||
from .types import lv_label_t
|
||||
from .widget import Widget, WidgetType
|
||||
from .types import LvText, WidgetType
|
||||
from .widget import Widget
|
||||
|
||||
|
||||
class LabelType(WidgetType):
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
CONF_LABEL,
|
||||
LvText("lv_label_t"),
|
||||
(CONF_MAIN, CONF_SCROLLBAR, CONF_SELECTED),
|
||||
TEXT_SCHEMA.extend(
|
||||
{
|
||||
cv.Optional(CONF_RECOLOR): lv_bool,
|
||||
|
@ -19,10 +30,6 @@ class LabelType(WidgetType):
|
|||
),
|
||||
)
|
||||
|
||||
@property
|
||||
def w_type(self):
|
||||
return lv_label_t
|
||||
|
||||
async def to_code(self, w: Widget, config):
|
||||
"""For a text object, create and set text"""
|
||||
if value := config.get(CONF_TEXT):
|
||||
|
|
|
@ -2,25 +2,42 @@ import esphome.codegen as cg
|
|||
from esphome.components.binary_sensor import BinarySensor
|
||||
from esphome.components.color import ColorStruct
|
||||
from esphome.components.font import Font
|
||||
from esphome.components.image import Image_
|
||||
from esphome.components.sensor import Sensor
|
||||
from esphome.components.text_sensor import TextSensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ARGS, CONF_COLOR, CONF_FORMAT
|
||||
from esphome.core import HexInt
|
||||
from esphome.cpp_generator import MockObj
|
||||
from esphome.cpp_types import uint32
|
||||
from esphome.helpers import cpp_string_escape
|
||||
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
|
||||
|
||||
from . import types as ty
|
||||
from .defines import LV_FONTS, LValidator, LvConstant
|
||||
from .defines import LV_FONTS, ConstantLiteral, LValidator, LvConstant, literal
|
||||
from .helpers import (
|
||||
esphome_fonts_used,
|
||||
lv_fonts_used,
|
||||
lvgl_components_required,
|
||||
requires_component,
|
||||
)
|
||||
from .lvcode import ConstantLiteral, lv_expr
|
||||
from .types import lv_font_t
|
||||
from .lvcode import lv_expr
|
||||
from .types import lv_font_t, lv_img_t
|
||||
|
||||
opacity_consts = LvConstant("LV_OPA_", "TRANSP", "COVER")
|
||||
|
||||
|
||||
@schema_extractor("one_of")
|
||||
def opacity_validator(value):
|
||||
if value == SCHEMA_EXTRACT:
|
||||
return opacity_consts.choices
|
||||
value = cv.Any(cv.percentage, opacity_consts.one_of)(value)
|
||||
if isinstance(value, float):
|
||||
return int(value * 255)
|
||||
return value
|
||||
|
||||
|
||||
opacity = LValidator(opacity_validator, uint32, retmapper=literal)
|
||||
|
||||
|
||||
@schema_extractor("one_of")
|
||||
|
@ -43,16 +60,22 @@ def color_retmapper(value):
|
|||
return lv_expr.color_from(MockObj(value))
|
||||
|
||||
|
||||
def pixels_or_percent(value):
|
||||
lv_color = LValidator(color, ty.lv_color_t, retmapper=color_retmapper)
|
||||
|
||||
|
||||
def pixels_or_percent_validator(value):
|
||||
"""A length in one axis - either a number (pixels) or a percentage"""
|
||||
if value == SCHEMA_EXTRACT:
|
||||
return ["pixels", "..%"]
|
||||
if isinstance(value, int):
|
||||
return str(cv.int_(value))
|
||||
return cv.int_(value)
|
||||
# Will throw an exception if not a percentage.
|
||||
return f"lv_pct({int(cv.percentage(value) * 100)})"
|
||||
|
||||
|
||||
pixels_or_percent = LValidator(pixels_or_percent_validator, uint32, retmapper=literal)
|
||||
|
||||
|
||||
def zoom(value):
|
||||
value = cv.float_range(0.1, 10.0)(value)
|
||||
return int(value * 256)
|
||||
|
@ -68,7 +91,7 @@ def angle(value):
|
|||
|
||||
|
||||
@schema_extractor("one_of")
|
||||
def size(value):
|
||||
def size_validator(value):
|
||||
"""A size in one axis - one of "size_content", a number (pixels) or a percentage"""
|
||||
if value == SCHEMA_EXTRACT:
|
||||
return ["size_content", "pixels", "..%"]
|
||||
|
@ -79,28 +102,58 @@ def size(value):
|
|||
return "LV_SIZE_CONTENT"
|
||||
raise cv.Invalid("must be 'size_content', a pixel position or a percentage")
|
||||
if isinstance(value, int):
|
||||
return str(cv.int_(value))
|
||||
return cv.int_(value)
|
||||
# Will throw an exception if not a percentage.
|
||||
return f"lv_pct({int(cv.percentage(value) * 100)})"
|
||||
|
||||
|
||||
size = LValidator(size_validator, uint32, retmapper=literal)
|
||||
|
||||
radius_consts = LvConstant("LV_RADIUS_", "CIRCLE")
|
||||
|
||||
|
||||
@schema_extractor("one_of")
|
||||
def opacity(value):
|
||||
consts = LvConstant("LV_OPA_", "TRANSP", "COVER")
|
||||
def radius_validator(value):
|
||||
if value == SCHEMA_EXTRACT:
|
||||
return consts.choices
|
||||
value = cv.Any(cv.percentage, consts.one_of)(value)
|
||||
return radius_consts.choices
|
||||
value = cv.Any(size, cv.percentage, radius_consts.one_of)(value)
|
||||
if isinstance(value, float):
|
||||
return int(value * 255)
|
||||
return value
|
||||
|
||||
|
||||
radius = LValidator(radius_validator, uint32, retmapper=literal)
|
||||
|
||||
|
||||
def id_name(value):
|
||||
if value == SCHEMA_EXTRACT:
|
||||
return "id"
|
||||
return cv.validate_id_name(value)
|
||||
|
||||
|
||||
def stop_value(value):
|
||||
return cv.int_range(0, 255)(value)
|
||||
|
||||
|
||||
lv_color = LValidator(color, ty.lv_color_t, retmapper=color_retmapper)
|
||||
lv_bool = LValidator(cv.boolean, cg.bool_, BinarySensor, "get_state()")
|
||||
lv_images_used = set()
|
||||
|
||||
|
||||
def image_validator(value):
|
||||
value = requires_component("image")(value)
|
||||
value = cv.use_id(Image_)(value)
|
||||
lv_images_used.add(value)
|
||||
return value
|
||||
|
||||
|
||||
lv_image = LValidator(
|
||||
image_validator,
|
||||
lv_img_t,
|
||||
retmapper=lambda x: lv_expr.img_from(MockObj(x)),
|
||||
requires="image",
|
||||
)
|
||||
lv_bool = LValidator(
|
||||
cv.boolean, cg.bool_, BinarySensor, "get_state()", retmapper=literal
|
||||
)
|
||||
|
||||
|
||||
def lvms_validator_(value):
|
||||
|
@ -145,26 +198,32 @@ lv_float = LValidator(cv.float_, cg.float_, Sensor, "get_state()")
|
|||
lv_int = LValidator(cv.int_, cg.int_, Sensor, "get_state()")
|
||||
|
||||
|
||||
def is_lv_font(font):
|
||||
return isinstance(font, str) and font.lower() in LV_FONTS
|
||||
|
||||
|
||||
class LvFont(LValidator):
|
||||
def __init__(self):
|
||||
def lv_builtin_font(value):
|
||||
fontval = cv.one_of(*LV_FONTS, lower=True)(value)
|
||||
lv_fonts_used.add(fontval)
|
||||
return "&lv_font_" + fontval
|
||||
return fontval
|
||||
|
||||
def validator(value):
|
||||
if value == SCHEMA_EXTRACT:
|
||||
return LV_FONTS
|
||||
if isinstance(value, str) and value.lower() in LV_FONTS:
|
||||
if is_lv_font(value):
|
||||
return lv_builtin_font(value)
|
||||
fontval = cv.use_id(Font)(value)
|
||||
esphome_fonts_used.add(fontval)
|
||||
return requires_component("font")(f"{fontval}_engine->get_lv_font()")
|
||||
return requires_component("font")(fontval)
|
||||
|
||||
super().__init__(validator, lv_font_t)
|
||||
|
||||
async def process(self, value, args=()):
|
||||
return ConstantLiteral(value)
|
||||
if is_lv_font(value):
|
||||
return ConstantLiteral(f"&lv_font_{value}")
|
||||
return ConstantLiteral(f"{value}_engine->get_lv_font()")
|
||||
|
||||
|
||||
lv_font = LvFont()
|
||||
|
|
|
@ -8,8 +8,8 @@ from esphome.cpp_generator import (
|
|||
AssignmentExpression,
|
||||
CallExpression,
|
||||
Expression,
|
||||
ExpressionStatement,
|
||||
LambdaExpression,
|
||||
Literal,
|
||||
MockObj,
|
||||
RawExpression,
|
||||
RawStatement,
|
||||
|
@ -19,7 +19,9 @@ from esphome.cpp_generator import (
|
|||
statement,
|
||||
)
|
||||
|
||||
from .defines import ConstantLiteral
|
||||
from .helpers import get_line_marks
|
||||
from .types import lv_group_t
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -105,29 +107,40 @@ class LambdaContext(CodeContext):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
parameters: list[tuple[SafeExpType, str]],
|
||||
return_type: SafeExpType = None,
|
||||
parameters: list[tuple[SafeExpType, str]] = None,
|
||||
return_type: SafeExpType = cg.void,
|
||||
capture: str = "",
|
||||
):
|
||||
super().__init__()
|
||||
self.code_list: list[Statement] = []
|
||||
self.parameters = parameters
|
||||
self.return_type = return_type
|
||||
self.capture = capture
|
||||
|
||||
def add(self, expression: Union[Expression, Statement]):
|
||||
self.code_list.append(expression)
|
||||
return expression
|
||||
|
||||
async def code(self) -> LambdaExpression:
|
||||
async def get_lambda(self) -> LambdaExpression:
|
||||
code_text = self.get_code()
|
||||
return await cg.process_lambda(
|
||||
Lambda("\n".join(code_text) + "\n\n"),
|
||||
self.parameters,
|
||||
capture=self.capture,
|
||||
return_type=self.return_type,
|
||||
)
|
||||
|
||||
def get_code(self):
|
||||
code_text = []
|
||||
for exp in self.code_list:
|
||||
text = str(statement(exp))
|
||||
text = text.rstrip()
|
||||
code_text.append(text)
|
||||
return await cg.process_lambda(
|
||||
Lambda("\n".join(code_text) + "\n\n"),
|
||||
self.parameters,
|
||||
return_type=self.return_type,
|
||||
)
|
||||
return code_text
|
||||
|
||||
def __enter__(self):
|
||||
super().__enter__()
|
||||
return self
|
||||
|
||||
|
||||
class LocalVariable(MockObj):
|
||||
|
@ -187,13 +200,18 @@ class MockLv:
|
|||
return result
|
||||
|
||||
def cond_if(self, expression: Expression):
|
||||
CodeContext.append(RawExpression(f"if({expression}) {{"))
|
||||
CodeContext.append(RawStatement(f"if {expression} {{"))
|
||||
|
||||
def cond_else(self):
|
||||
CodeContext.append(RawExpression("} else {"))
|
||||
CodeContext.append(RawStatement("} else {"))
|
||||
|
||||
def cond_endif(self):
|
||||
CodeContext.append(RawExpression("}"))
|
||||
CodeContext.append(RawStatement("}"))
|
||||
|
||||
|
||||
class ReturnStatement(ExpressionStatement):
|
||||
def __str__(self):
|
||||
return f"return {self.expression};"
|
||||
|
||||
|
||||
class LvExpr(MockLv):
|
||||
|
@ -210,6 +228,7 @@ lv = MockLv("lv_")
|
|||
lv_expr = LvExpr("lv_")
|
||||
# Mock for lv_obj_ calls
|
||||
lv_obj = MockLv("lv_obj_")
|
||||
lvgl_comp = MockObj("lvgl_comp", "->")
|
||||
|
||||
|
||||
# equivalent to cg.add() for the lvgl init context
|
||||
|
@ -226,12 +245,19 @@ def lv_assign(target, expression):
|
|||
lv_add(RawExpression(f"{target} = {expression}"))
|
||||
|
||||
|
||||
class ConstantLiteral(Literal):
|
||||
__slots__ = ("constant",)
|
||||
lv_groups = {} # Widget group names
|
||||
|
||||
def __init__(self, constant: str):
|
||||
super().__init__()
|
||||
self.constant = constant
|
||||
|
||||
def __str__(self):
|
||||
return self.constant
|
||||
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] = ConstantLiteral(fullname)
|
||||
return lv_groups[name]
|
||||
|
|
|
@ -19,13 +19,35 @@ void LvglComponent::draw_buffer_(const lv_area_t *area, const uint8_t *ptr) {
|
|||
}
|
||||
|
||||
void LvglComponent::flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) {
|
||||
if (!this->paused_) {
|
||||
auto now = millis();
|
||||
this->draw_buffer_(area, (const uint8_t *) color_p);
|
||||
ESP_LOGV(TAG, "flush_cb, area=%d/%d, %d/%d took %dms", area->x1, area->y1, lv_area_get_width(area),
|
||||
lv_area_get_height(area), (int) (millis() - now));
|
||||
}
|
||||
lv_disp_flush_ready(disp_drv);
|
||||
}
|
||||
|
||||
void LvglComponent::write_random_() {
|
||||
// length of 2 lines in 32 bit units
|
||||
// we write 2 lines for the benefit of displays that won't write one line at a time.
|
||||
size_t line_len = this->disp_drv_.hor_res * LV_COLOR_DEPTH / 8 / 4 * 2;
|
||||
for (size_t i = 0; i != line_len; i++) {
|
||||
((uint32_t *) (this->draw_buf_.buf1))[i] = random_uint32();
|
||||
}
|
||||
lv_area_t area;
|
||||
area.x1 = 0;
|
||||
area.x2 = this->disp_drv_.hor_res - 1;
|
||||
if (this->snow_line_ == this->disp_drv_.ver_res / 2) {
|
||||
area.y1 = static_cast<lv_coord_t>(random_uint32() % (this->disp_drv_.ver_res / 2) * 2);
|
||||
} else {
|
||||
area.y1 = this->snow_line_++ * 2;
|
||||
}
|
||||
// write 2 lines
|
||||
area.y2 = area.y1 + 1;
|
||||
this->draw_buffer_(&area, (const uint8_t *) this->draw_buf_.buf1);
|
||||
}
|
||||
|
||||
void LvglComponent::setup() {
|
||||
ESP_LOGCONFIG(TAG, "LVGL Setup starts");
|
||||
#if LV_USE_LOG
|
||||
|
@ -38,7 +60,9 @@ void LvglComponent::setup() {
|
|||
auto buf_bytes = buffer_pixels * LV_COLOR_DEPTH / 8;
|
||||
auto *buf = lv_custom_mem_alloc(buf_bytes);
|
||||
if (buf == nullptr) {
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR
|
||||
ESP_LOGE(TAG, "Malloc failed to allocate %zu bytes", buf_bytes);
|
||||
#endif
|
||||
this->mark_failed();
|
||||
this->status_set_error("Memory allocation failure");
|
||||
return;
|
||||
|
@ -72,10 +96,53 @@ void LvglComponent::setup() {
|
|||
ESP_LOGV(TAG, "sw_rotate = %d, rotated=%d", this->disp_drv_.sw_rotate, this->disp_drv_.rotated);
|
||||
this->disp_ = lv_disp_drv_register(&this->disp_drv_);
|
||||
for (const auto &v : this->init_lambdas_)
|
||||
v(this->disp_);
|
||||
v(this);
|
||||
lv_disp_trig_activity(this->disp_);
|
||||
ESP_LOGCONFIG(TAG, "LVGL Setup complete");
|
||||
}
|
||||
|
||||
#ifdef USE_LVGL_IMAGE
|
||||
lv_img_dsc_t *lv_img_from(image::Image *src, lv_img_dsc_t *img_dsc) {
|
||||
if (img_dsc == nullptr)
|
||||
img_dsc = new lv_img_dsc_t(); // NOLINT
|
||||
img_dsc->header.always_zero = 0;
|
||||
img_dsc->header.reserved = 0;
|
||||
img_dsc->header.w = src->get_width();
|
||||
img_dsc->header.h = src->get_height();
|
||||
img_dsc->data = src->get_data_start();
|
||||
img_dsc->data_size = image_type_to_width_stride(img_dsc->header.w * img_dsc->header.h, src->get_type());
|
||||
switch (src->get_type()) {
|
||||
case image::IMAGE_TYPE_BINARY:
|
||||
img_dsc->header.cf = LV_IMG_CF_ALPHA_1BIT;
|
||||
break;
|
||||
|
||||
case image::IMAGE_TYPE_GRAYSCALE:
|
||||
img_dsc->header.cf = LV_IMG_CF_ALPHA_8BIT;
|
||||
break;
|
||||
|
||||
case image::IMAGE_TYPE_RGB24:
|
||||
img_dsc->header.cf = LV_IMG_CF_RGB888;
|
||||
break;
|
||||
|
||||
case image::IMAGE_TYPE_RGB565:
|
||||
#if LV_COLOR_DEPTH == 16
|
||||
img_dsc->header.cf = src->has_transparency() ? LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED : LV_IMG_CF_TRUE_COLOR;
|
||||
#else
|
||||
img_dsc->header.cf = LV_IMG_CF_RGB565;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case image::IMAGE_TYPE_RGBA:
|
||||
#if LV_COLOR_DEPTH == 32
|
||||
img_dsc->header.cf = LV_IMG_CF_TRUE_COLOR;
|
||||
#else
|
||||
img_dsc->header.cf = LV_IMG_CF_RGBA8888;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
return img_dsc;
|
||||
}
|
||||
#endif
|
||||
} // namespace lvgl
|
||||
} // namespace esphome
|
||||
|
||||
|
@ -85,7 +152,9 @@ size_t lv_millis(void) { return esphome::millis(); }
|
|||
void *lv_custom_mem_alloc(size_t size) {
|
||||
auto *ptr = malloc(size); // NOLINT
|
||||
if (ptr == nullptr) {
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR
|
||||
esphome::ESP_LOGE(esphome::lvgl::TAG, "Failed to allocate %zu bytes", size);
|
||||
#endif
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
@ -102,7 +171,9 @@ void *lv_custom_mem_alloc(size_t size) {
|
|||
ptr = heap_caps_malloc(size, cap_bits);
|
||||
}
|
||||
if (ptr == nullptr) {
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR
|
||||
esphome::ESP_LOGE(esphome::lvgl::TAG, "Failed to allocate %zu bytes", size);
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
#ifdef ESPHOME_LOG_HAS_VERBOSE
|
||||
|
|
|
@ -1,42 +1,55 @@
|
|||
#pragma once
|
||||
#include "esphome/core/defines.h"
|
||||
#ifdef USE_LVGL
|
||||
|
||||
#ifdef USE_LVGL_BINARY_SENSOR
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#endif // USE_LVGL_BINARY_SENSOR
|
||||
#ifdef USE_LVGL_ROTARY_ENCODER
|
||||
#include "esphome/components/rotary_encoder/rotary_encoder.h"
|
||||
#endif // USE_LVGL_ROTARY_ENCODER
|
||||
|
||||
// required for clang-tidy
|
||||
#ifndef LV_CONF_H
|
||||
#define LV_CONF_SKIP 1 // NOLINT
|
||||
#endif
|
||||
#endif // LV_CONF_H
|
||||
|
||||
#include "esphome/components/display/display.h"
|
||||
#include "esphome/components/display/display_color_utils.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <lvgl.h>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#ifdef USE_LVGL_IMAGE
|
||||
#include "esphome/components/image/image.h"
|
||||
#endif // USE_LVGL_IMAGE
|
||||
|
||||
#ifdef USE_LVGL_FONT
|
||||
#include "esphome/components/font/font.h"
|
||||
#endif
|
||||
#endif // USE_LVGL_FONT
|
||||
#ifdef USE_LVGL_TOUCHSCREEN
|
||||
#include "esphome/components/touchscreen/touchscreen.h"
|
||||
#endif // USE_LVGL_TOUCHSCREEN
|
||||
|
||||
namespace esphome {
|
||||
namespace lvgl {
|
||||
|
||||
extern lv_event_code_t lv_custom_event; // NOLINT
|
||||
#ifdef USE_LVGL_COLOR
|
||||
static lv_color_t lv_color_from(Color color) { return lv_color_make(color.red, color.green, color.blue); }
|
||||
#endif
|
||||
inline lv_color_t lv_color_from(Color color) { return lv_color_make(color.red, color.green, color.blue); }
|
||||
#endif // USE_LVGL_COLOR
|
||||
#if LV_COLOR_DEPTH == 16
|
||||
static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BITNESS_565;
|
||||
#elif LV_COLOR_DEPTH == 32
|
||||
static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BITNESS_888;
|
||||
#else
|
||||
#else // LV_COLOR_DEPTH
|
||||
static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BITNESS_332;
|
||||
#endif
|
||||
#endif // LV_COLOR_DEPTH
|
||||
|
||||
// Parent class for things that wrap an LVGL object
|
||||
class LvCompound {
|
||||
class LvCompound final {
|
||||
public:
|
||||
virtual void set_obj(lv_obj_t *lv_obj) { this->obj = lv_obj; }
|
||||
void set_obj(lv_obj_t *lv_obj) { this->obj = lv_obj; }
|
||||
lv_obj_t *obj{};
|
||||
};
|
||||
|
||||
|
@ -45,6 +58,15 @@ using set_value_lambda_t = std::function<void(float)>;
|
|||
using event_callback_t = void(_lv_event_t *);
|
||||
using text_lambda_t = std::function<const char *()>;
|
||||
|
||||
template<typename... Ts> class ObjUpdateAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit ObjUpdateAction(std::function<void(Ts...)> &&lamb) : lamb_(std::move(lamb)) {}
|
||||
|
||||
void play(Ts... x) override { this->lamb_(x...); }
|
||||
|
||||
protected:
|
||||
std::function<void(Ts...)> lamb_;
|
||||
};
|
||||
#ifdef USE_LVGL_FONT
|
||||
class FontEngine {
|
||||
public:
|
||||
|
@ -63,6 +85,9 @@ class FontEngine {
|
|||
lv_font_t lv_font_{};
|
||||
};
|
||||
#endif // USE_LVGL_FONT
|
||||
#ifdef USE_LVGL_IMAGE
|
||||
lv_img_dsc_t *lv_img_from(image::Image *src, lv_img_dsc_t *img_dsc = nullptr);
|
||||
#endif // USE_LVGL_IMAGE
|
||||
|
||||
class LvglComponent : public PollingComponent {
|
||||
constexpr static const char *const TAG = "lvgl";
|
||||
|
@ -88,32 +113,197 @@ class LvglComponent : public PollingComponent {
|
|||
area->y2++;
|
||||
}
|
||||
|
||||
void loop() override { lv_timer_handler_run_in_period(5); }
|
||||
void setup() override;
|
||||
|
||||
void update() override {}
|
||||
void update() override {
|
||||
// update indicators
|
||||
if (this->paused_) {
|
||||
return;
|
||||
}
|
||||
this->idle_callbacks_.call(lv_disp_get_inactive_time(this->disp_));
|
||||
}
|
||||
|
||||
void loop() override {
|
||||
if (this->paused_) {
|
||||
if (this->show_snow_)
|
||||
this->write_random_();
|
||||
}
|
||||
lv_timer_handler_run_in_period(5);
|
||||
}
|
||||
|
||||
void add_on_idle_callback(std::function<void(uint32_t)> &&callback) {
|
||||
this->idle_callbacks_.add(std::move(callback));
|
||||
}
|
||||
void add_display(display::Display *display) { this->displays_.push_back(display); }
|
||||
void add_init_lambda(const std::function<void(lv_disp_t *)> &lamb) { this->init_lambdas_.push_back(lamb); }
|
||||
void add_init_lambda(const std::function<void(LvglComponent *)> &lamb) { this->init_lambdas_.push_back(lamb); }
|
||||
void dump_config() override;
|
||||
void set_full_refresh(bool full_refresh) { this->full_refresh_ = full_refresh; }
|
||||
bool is_idle(uint32_t idle_ms) { return lv_disp_get_inactive_time(this->disp_) > idle_ms; }
|
||||
void set_buffer_frac(size_t frac) { this->buffer_frac_ = frac; }
|
||||
lv_disp_t *get_disp() { return this->disp_; }
|
||||
void set_paused(bool paused, bool show_snow) {
|
||||
this->paused_ = paused;
|
||||
this->show_snow_ = show_snow;
|
||||
this->snow_line_ = 0;
|
||||
if (!paused && lv_scr_act() != nullptr) {
|
||||
lv_disp_trig_activity(this->disp_); // resets the inactivity time
|
||||
lv_obj_invalidate(lv_scr_act());
|
||||
}
|
||||
}
|
||||
|
||||
void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event) {
|
||||
lv_obj_add_event_cb(obj, callback, event, this);
|
||||
if (event == LV_EVENT_VALUE_CHANGED) {
|
||||
lv_obj_add_event_cb(obj, callback, lv_custom_event, this);
|
||||
}
|
||||
}
|
||||
bool is_paused() const { return this->paused_; }
|
||||
|
||||
protected:
|
||||
void write_random_();
|
||||
void draw_buffer_(const lv_area_t *area, const uint8_t *ptr);
|
||||
void flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
|
||||
std::vector<display::Display *> displays_{};
|
||||
lv_disp_draw_buf_t draw_buf_{};
|
||||
lv_disp_drv_t disp_drv_{};
|
||||
lv_disp_t *disp_{};
|
||||
bool paused_{};
|
||||
bool show_snow_{};
|
||||
lv_coord_t snow_line_{};
|
||||
|
||||
std::vector<std::function<void(lv_disp_t *)>> init_lambdas_;
|
||||
std::vector<std::function<void(LvglComponent *lv_component)>> init_lambdas_;
|
||||
CallbackManager<void(uint32_t)> idle_callbacks_{};
|
||||
size_t buffer_frac_{1};
|
||||
bool full_refresh_{};
|
||||
};
|
||||
|
||||
class IdleTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit IdleTrigger(LvglComponent *parent, TemplatableValue<uint32_t> timeout) : timeout_(std::move(timeout)) {
|
||||
parent->add_on_idle_callback([this](uint32_t idle_time) {
|
||||
if (!this->is_idle_ && idle_time > this->timeout_.value()) {
|
||||
this->is_idle_ = true;
|
||||
this->trigger();
|
||||
} else if (this->is_idle_ && idle_time < this->timeout_.value()) {
|
||||
this->is_idle_ = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected:
|
||||
TemplatableValue<uint32_t> timeout_;
|
||||
bool is_idle_{};
|
||||
};
|
||||
|
||||
template<typename... Ts> class LvglAction : public Action<Ts...>, public Parented<LvglComponent> {
|
||||
public:
|
||||
explicit LvglAction(std::function<void(LvglComponent *)> &&lamb) : action_(std::move(lamb)) {}
|
||||
void play(Ts... x) override { this->action_(this->parent_); }
|
||||
|
||||
protected:
|
||||
std::function<void(LvglComponent *)> action_{};
|
||||
};
|
||||
|
||||
template<typename... Ts> class LvglCondition : public Condition<Ts...>, public Parented<LvglComponent> {
|
||||
public:
|
||||
LvglCondition(std::function<bool(LvglComponent *)> &&condition_lambda)
|
||||
: condition_lambda_(std::move(condition_lambda)) {}
|
||||
bool check(Ts... x) override { return this->condition_lambda_(this->parent_); }
|
||||
|
||||
protected:
|
||||
std::function<bool(LvglComponent *)> condition_lambda_{};
|
||||
};
|
||||
|
||||
#ifdef USE_LVGL_TOUCHSCREEN
|
||||
class LVTouchListener : public touchscreen::TouchListener, public Parented<LvglComponent> {
|
||||
public:
|
||||
LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time) {
|
||||
lv_indev_drv_init(&this->drv_);
|
||||
this->drv_.long_press_repeat_time = long_press_repeat_time;
|
||||
this->drv_.long_press_time = long_press_time;
|
||||
this->drv_.type = LV_INDEV_TYPE_POINTER;
|
||||
this->drv_.user_data = this;
|
||||
this->drv_.read_cb = [](lv_indev_drv_t *d, lv_indev_data_t *data) {
|
||||
auto *l = static_cast<LVTouchListener *>(d->user_data);
|
||||
if (l->touch_pressed_) {
|
||||
data->point.x = l->touch_point_.x;
|
||||
data->point.y = l->touch_point_.y;
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
} else {
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
};
|
||||
}
|
||||
void update(const touchscreen::TouchPoints_t &tpoints) override {
|
||||
this->touch_pressed_ = !this->parent_->is_paused() && !tpoints.empty();
|
||||
if (this->touch_pressed_)
|
||||
this->touch_point_ = tpoints[0];
|
||||
}
|
||||
void release() override { touch_pressed_ = false; }
|
||||
lv_indev_drv_t *get_drv() { return &this->drv_; }
|
||||
|
||||
protected:
|
||||
lv_indev_drv_t drv_{};
|
||||
touchscreen::TouchPoint touch_point_{};
|
||||
bool touch_pressed_{};
|
||||
};
|
||||
#endif // USE_LVGL_TOUCHSCREEN
|
||||
|
||||
#ifdef USE_LVGL_KEY_LISTENER
|
||||
class LVEncoderListener : public Parented<LvglComponent> {
|
||||
public:
|
||||
LVEncoderListener(lv_indev_type_t type, uint16_t lpt, uint16_t lprt) {
|
||||
lv_indev_drv_init(&this->drv_);
|
||||
this->drv_.type = type;
|
||||
this->drv_.user_data = this;
|
||||
this->drv_.long_press_time = lpt;
|
||||
this->drv_.long_press_repeat_time = lprt;
|
||||
this->drv_.read_cb = [](lv_indev_drv_t *d, lv_indev_data_t *data) {
|
||||
auto *l = static_cast<LVEncoderListener *>(d->user_data);
|
||||
data->state = l->pressed_ ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
|
||||
data->key = l->key_;
|
||||
data->enc_diff = (int16_t) (l->count_ - l->last_count_);
|
||||
l->last_count_ = l->count_;
|
||||
data->continue_reading = false;
|
||||
};
|
||||
}
|
||||
|
||||
void set_left_button(binary_sensor::BinarySensor *left_button) {
|
||||
left_button->add_on_state_callback([this](bool state) { this->event(LV_KEY_LEFT, state); });
|
||||
}
|
||||
void set_right_button(binary_sensor::BinarySensor *right_button) {
|
||||
right_button->add_on_state_callback([this](bool state) { this->event(LV_KEY_RIGHT, state); });
|
||||
}
|
||||
|
||||
void set_enter_button(binary_sensor::BinarySensor *enter_button) {
|
||||
enter_button->add_on_state_callback([this](bool state) { this->event(LV_KEY_ENTER, state); });
|
||||
}
|
||||
|
||||
void set_sensor(rotary_encoder::RotaryEncoderSensor *sensor) {
|
||||
sensor->register_listener([this](int32_t count) { this->set_count(count); });
|
||||
}
|
||||
|
||||
void event(int key, bool pressed) {
|
||||
if (!this->parent_->is_paused()) {
|
||||
this->pressed_ = pressed;
|
||||
this->key_ = key;
|
||||
}
|
||||
}
|
||||
|
||||
void set_count(int32_t count) {
|
||||
if (!this->parent_->is_paused())
|
||||
this->count_ = count;
|
||||
}
|
||||
|
||||
lv_indev_drv_t *get_drv() { return &this->drv_; }
|
||||
|
||||
protected:
|
||||
lv_indev_drv_t drv_{};
|
||||
bool pressed_{};
|
||||
int32_t count_{};
|
||||
int32_t last_count_{};
|
||||
int key_{};
|
||||
};
|
||||
#endif // USE_LVGL_KEY_LISTENER
|
||||
} // namespace lvgl
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
from .defines import CONF_OBJ
|
||||
from .types import lv_obj_t
|
||||
from .widget import WidgetType
|
||||
from esphome import automation
|
||||
|
||||
from .automation import update_to_code
|
||||
from .defines import CONF_MAIN, CONF_OBJ
|
||||
from .schemas import create_modify_schema
|
||||
from .types import ObjUpdateAction, WidgetType, lv_obj_t
|
||||
|
||||
|
||||
class ObjType(WidgetType):
|
||||
|
@ -9,14 +12,17 @@ class ObjType(WidgetType):
|
|||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(CONF_OBJ, schema={}, modify_schema={})
|
||||
|
||||
@property
|
||||
def w_type(self):
|
||||
return lv_obj_t
|
||||
super().__init__(CONF_OBJ, lv_obj_t, (CONF_MAIN,), schema={}, modify_schema={})
|
||||
|
||||
async def to_code(self, w, config):
|
||||
return []
|
||||
|
||||
|
||||
obj_spec = ObjType()
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"lvgl.widget.update", ObjUpdateAction, create_modify_schema(obj_spec)
|
||||
)
|
||||
async def obj_update_to_code(config, action_id, template_arg, args):
|
||||
return await update_to_code(config, action_id, template_arg, args)
|
||||
|
|
62
esphome/components/lvgl/rotary_encoders.py
Normal file
62
esphome/components/lvgl/rotary_encoders.py
Normal file
|
@ -0,0 +1,62 @@
|
|||
import esphome.codegen as cg
|
||||
from esphome.components.binary_sensor import BinarySensor
|
||||
from esphome.components.rotary_encoder.sensor import RotaryEncoderSensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_GROUP, CONF_ID, CONF_SENSOR
|
||||
|
||||
from .defines import (
|
||||
CONF_ENTER_BUTTON,
|
||||
CONF_LEFT_BUTTON,
|
||||
CONF_LONG_PRESS_REPEAT_TIME,
|
||||
CONF_LONG_PRESS_TIME,
|
||||
CONF_RIGHT_BUTTON,
|
||||
CONF_ROTARY_ENCODERS,
|
||||
)
|
||||
from .helpers import lvgl_components_required
|
||||
from .lvcode import add_group, lv, lv_add, lv_expr
|
||||
from .schemas import ENCODER_SCHEMA
|
||||
from .types import lv_indev_type_t
|
||||
|
||||
ROTARY_ENCODER_CONFIG = cv.ensure_list(
|
||||
ENCODER_SCHEMA.extend(
|
||||
{
|
||||
cv.Required(CONF_ENTER_BUTTON): cv.use_id(BinarySensor),
|
||||
cv.Required(CONF_SENSOR): cv.Any(
|
||||
cv.use_id(RotaryEncoderSensor),
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_LEFT_BUTTON): cv.use_id(BinarySensor),
|
||||
cv.Required(CONF_RIGHT_BUTTON): cv.use_id(BinarySensor),
|
||||
}
|
||||
),
|
||||
),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def rotary_encoders_to_code(var, config):
|
||||
for enc_conf in config.get(CONF_ROTARY_ENCODERS, ()):
|
||||
lvgl_components_required.add("KEY_LISTENER")
|
||||
lvgl_components_required.add("ROTARY_ENCODER")
|
||||
lpt = enc_conf[CONF_LONG_PRESS_TIME].total_milliseconds
|
||||
lprt = enc_conf[CONF_LONG_PRESS_REPEAT_TIME].total_milliseconds
|
||||
listener = cg.new_Pvariable(
|
||||
enc_conf[CONF_ID], lv_indev_type_t.LV_INDEV_TYPE_ENCODER, lpt, lprt
|
||||
)
|
||||
await cg.register_parented(listener, var)
|
||||
if sensor_config := enc_conf.get(CONF_SENSOR):
|
||||
if isinstance(sensor_config, dict):
|
||||
b_sensor = await cg.get_variable(sensor_config[CONF_LEFT_BUTTON])
|
||||
cg.add(listener.set_left_button(b_sensor))
|
||||
b_sensor = await cg.get_variable(sensor_config[CONF_RIGHT_BUTTON])
|
||||
cg.add(listener.set_right_button(b_sensor))
|
||||
else:
|
||||
sensor_config = await cg.get_variable(sensor_config)
|
||||
lv_add(listener.set_sensor(sensor_config))
|
||||
b_sensor = await cg.get_variable(enc_conf[CONF_ENTER_BUTTON])
|
||||
cg.add(listener.set_enter_button(b_sensor))
|
||||
if group := add_group(enc_conf.get(CONF_GROUP)):
|
||||
lv.indev_set_group(lv_expr.indev_drv_register(listener.get_drv()), group)
|
||||
else:
|
||||
lv.indev_drv_register(listener.get_drv())
|
|
@ -1,17 +1,22 @@
|
|||
from esphome import config_validation as cv
|
||||
from esphome.const import CONF_ARGS, CONF_FORMAT, CONF_ID, CONF_STATE, CONF_TYPE
|
||||
from esphome.automation import Trigger, validate_automation
|
||||
from esphome.const import (
|
||||
CONF_ARGS,
|
||||
CONF_FORMAT,
|
||||
CONF_GROUP,
|
||||
CONF_ID,
|
||||
CONF_ON_VALUE,
|
||||
CONF_STATE,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_TYPE,
|
||||
)
|
||||
from esphome.core import TimePeriod
|
||||
from esphome.schema_extractors import SCHEMA_EXTRACT
|
||||
|
||||
from . import defines as df, lv_validation as lvalid, types as ty
|
||||
from .defines import WIDGET_PARTS
|
||||
from .helpers import (
|
||||
REQUIRED_COMPONENTS,
|
||||
add_lv_use,
|
||||
requires_component,
|
||||
validate_printf,
|
||||
)
|
||||
from .lv_validation import lv_font
|
||||
from .types import WIDGET_TYPES, get_widget_type
|
||||
from .helpers import add_lv_use, requires_component, validate_printf
|
||||
from .lv_validation import id_name, lv_font
|
||||
from .types import WIDGET_TYPES, WidgetType
|
||||
|
||||
# A schema for text properties
|
||||
TEXT_SCHEMA = cv.Schema(
|
||||
|
@ -33,6 +38,28 @@ TEXT_SCHEMA = cv.Schema(
|
|||
}
|
||||
)
|
||||
|
||||
ACTION_SCHEMA = cv.maybe_simple_value(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(ty.lv_pseudo_button_t),
|
||||
},
|
||||
key=CONF_ID,
|
||||
)
|
||||
|
||||
PRESS_TIME = cv.All(
|
||||
lvalid.lv_milliseconds, cv.Range(max=TimePeriod(milliseconds=65535))
|
||||
)
|
||||
|
||||
ENCODER_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.All(
|
||||
cv.declare_id(ty.LVEncoderListener), requires_component("binary_sensor")
|
||||
),
|
||||
cv.Optional(CONF_GROUP): lvalid.id_name,
|
||||
cv.Optional(df.CONF_LONG_PRESS_TIME, default="400ms"): PRESS_TIME,
|
||||
cv.Optional(df.CONF_LONG_PRESS_REPEAT_TIME, default="100ms"): PRESS_TIME,
|
||||
}
|
||||
)
|
||||
|
||||
# All LVGL styles and their validators
|
||||
STYLE_PROPS = {
|
||||
"align": df.CHILD_ALIGNMENTS.one_of,
|
||||
|
@ -46,9 +73,10 @@ STYLE_PROPS = {
|
|||
"bg_dither_mode": df.LvConstant("LV_DITHER_", "NONE", "ORDERED", "ERR_DIFF").one_of,
|
||||
"bg_grad_dir": df.LvConstant("LV_GRAD_DIR_", "NONE", "HOR", "VER").one_of,
|
||||
"bg_grad_stop": lvalid.stop_value,
|
||||
"bg_img_opa": lvalid.opacity,
|
||||
"bg_img_recolor": lvalid.lv_color,
|
||||
"bg_img_recolor_opa": lvalid.opacity,
|
||||
"bg_image_opa": lvalid.opacity,
|
||||
"bg_image_recolor": lvalid.lv_color,
|
||||
"bg_image_recolor_opa": lvalid.opacity,
|
||||
"bg_image_src": lvalid.lv_image,
|
||||
"bg_main_stop": lvalid.stop_value,
|
||||
"bg_opa": lvalid.opacity,
|
||||
"border_color": lvalid.lv_color,
|
||||
|
@ -60,8 +88,8 @@ STYLE_PROPS = {
|
|||
"border_width": cv.positive_int,
|
||||
"clip_corner": lvalid.lv_bool,
|
||||
"height": lvalid.size,
|
||||
"img_recolor": lvalid.lv_color,
|
||||
"img_recolor_opa": lvalid.opacity,
|
||||
"image_recolor": lvalid.lv_color,
|
||||
"image_recolor_opa": lvalid.opacity,
|
||||
"line_width": cv.positive_int,
|
||||
"line_dash_width": cv.positive_int,
|
||||
"line_dash_gap": cv.positive_int,
|
||||
|
@ -108,12 +136,21 @@ STYLE_PROPS = {
|
|||
"max_width": lvalid.pixels_or_percent,
|
||||
"min_height": lvalid.pixels_or_percent,
|
||||
"min_width": lvalid.pixels_or_percent,
|
||||
"radius": cv.Any(lvalid.size, df.LvConstant("LV_RADIUS_", "CIRCLE").one_of),
|
||||
"radius": lvalid.radius,
|
||||
"width": lvalid.size,
|
||||
"x": lvalid.pixels_or_percent,
|
||||
"y": lvalid.pixels_or_percent,
|
||||
}
|
||||
|
||||
STYLE_REMAP = {
|
||||
"bg_image_opa": "bg_img_opa",
|
||||
"bg_image_recolor": "bg_img_recolor",
|
||||
"bg_image_recolor_opa": "bg_img_recolor_opa",
|
||||
"bg_image_src": "bg_img_src",
|
||||
"image_recolor": "img_recolor",
|
||||
"image_recolor_opa": "img_recolor_opa",
|
||||
}
|
||||
|
||||
# Complete object style schema
|
||||
STYLE_SCHEMA = cv.Schema({cv.Optional(k): v for k, v in STYLE_PROPS.items()}).extend(
|
||||
{
|
||||
|
@ -132,25 +169,56 @@ SET_STATE_SCHEMA = cv.Schema(
|
|||
{cv.Optional(state): lvalid.lv_bool for state in df.STATES}
|
||||
)
|
||||
# Setting object flags
|
||||
FLAG_SCHEMA = cv.Schema({cv.Optional(flag): cv.boolean for flag in df.OBJ_FLAGS})
|
||||
FLAG_SCHEMA = cv.Schema({cv.Optional(flag): lvalid.lv_bool for flag in df.OBJ_FLAGS})
|
||||
FLAG_LIST = cv.ensure_list(df.LvConstant("LV_OBJ_FLAG_", *df.OBJ_FLAGS).one_of)
|
||||
|
||||
|
||||
def part_schema(widget_type):
|
||||
def part_schema(widget_type: WidgetType):
|
||||
"""
|
||||
Generate a schema for the various parts (e.g. main:, indicator:) of a widget type
|
||||
:param widget_type: The type of widget to generate for
|
||||
:return:
|
||||
"""
|
||||
parts = WIDGET_PARTS.get(widget_type)
|
||||
if parts is None:
|
||||
parts = (df.CONF_MAIN,)
|
||||
parts = widget_type.parts
|
||||
return cv.Schema({cv.Optional(part): STATE_SCHEMA for part in parts}).extend(
|
||||
STATE_SCHEMA
|
||||
)
|
||||
|
||||
|
||||
def obj_schema(widget_type: str):
|
||||
def automation_schema(typ: ty.LvType):
|
||||
if typ.has_on_value:
|
||||
events = df.LV_EVENT_TRIGGERS + (CONF_ON_VALUE,)
|
||||
else:
|
||||
events = df.LV_EVENT_TRIGGERS
|
||||
if isinstance(typ, ty.LvType):
|
||||
template = Trigger.template(typ.get_arg_type())
|
||||
else:
|
||||
template = Trigger.template()
|
||||
return {
|
||||
cv.Optional(event): validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(template),
|
||||
}
|
||||
)
|
||||
for event in events
|
||||
}
|
||||
|
||||
|
||||
def create_modify_schema(widget_type):
|
||||
return (
|
||||
part_schema(widget_type)
|
||||
.extend(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(widget_type),
|
||||
cv.Optional(CONF_STATE): SET_STATE_SCHEMA,
|
||||
}
|
||||
)
|
||||
.extend(FLAG_SCHEMA)
|
||||
.extend(widget_type.modify_schema)
|
||||
)
|
||||
|
||||
|
||||
def obj_schema(widget_type: WidgetType):
|
||||
"""
|
||||
Create a schema for a widget type itself i.e. no allowance for children
|
||||
:param widget_type:
|
||||
|
@ -160,10 +228,12 @@ def obj_schema(widget_type: str):
|
|||
part_schema(widget_type)
|
||||
.extend(FLAG_SCHEMA)
|
||||
.extend(ALIGN_TO_SCHEMA)
|
||||
.extend(automation_schema(widget_type.w_type))
|
||||
.extend(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_STATE): SET_STATE_SCHEMA,
|
||||
cv.Optional(CONF_GROUP): id_name,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
@ -187,13 +257,19 @@ STYLED_TEXT_SCHEMA = cv.maybe_simple_value(
|
|||
STYLE_SCHEMA.extend(TEXT_SCHEMA), key=df.CONF_TEXT
|
||||
)
|
||||
|
||||
# For use by platform components
|
||||
LVGL_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(df.CONF_LVGL_ID): cv.use_id(ty.LvglComponent),
|
||||
}
|
||||
)
|
||||
|
||||
ALL_STYLES = {
|
||||
**STYLE_PROPS,
|
||||
}
|
||||
|
||||
|
||||
def container_validator(schema, widget_type):
|
||||
def container_validator(schema, widget_type: WidgetType):
|
||||
"""
|
||||
Create a validator for a container given the widget type
|
||||
:param schema: Base schema to extend
|
||||
|
@ -203,13 +279,16 @@ def container_validator(schema, widget_type):
|
|||
|
||||
def validator(value):
|
||||
result = schema
|
||||
if w_sch := WIDGET_TYPES[widget_type].schema:
|
||||
if w_sch := widget_type.schema:
|
||||
result = result.extend(w_sch)
|
||||
if value and (layout := value.get(df.CONF_LAYOUT)):
|
||||
if not isinstance(layout, dict):
|
||||
raise cv.Invalid("Layout value must be a dict")
|
||||
ltype = layout.get(CONF_TYPE)
|
||||
add_lv_use(ltype)
|
||||
result = result.extend(
|
||||
{cv.Optional(df.CONF_WIDGETS): cv.ensure_list(any_widget_schema())}
|
||||
)
|
||||
if value == SCHEMA_EXTRACT:
|
||||
return result
|
||||
return result(value)
|
||||
|
@ -217,7 +296,7 @@ def container_validator(schema, widget_type):
|
|||
return validator
|
||||
|
||||
|
||||
def container_schema(widget_type, extras=None):
|
||||
def container_schema(widget_type: WidgetType, extras=None):
|
||||
"""
|
||||
Create a schema for a container widget of a given type. All obj properties are available, plus
|
||||
the extras passed in, plus any defined for the specific widget being specified.
|
||||
|
@ -225,15 +304,16 @@ def container_schema(widget_type, extras=None):
|
|||
:param extras: Additional options to be made available, e.g. layout properties for children
|
||||
:return: The schema for this type of widget.
|
||||
"""
|
||||
lv_type = get_widget_type(widget_type)
|
||||
schema = obj_schema(widget_type).extend({cv.GenerateID(): cv.declare_id(lv_type)})
|
||||
schema = obj_schema(widget_type).extend(
|
||||
{cv.GenerateID(): cv.declare_id(widget_type.w_type)}
|
||||
)
|
||||
if extras:
|
||||
schema = schema.extend(extras)
|
||||
# Delayed evaluation for recursion
|
||||
return container_validator(schema, widget_type)
|
||||
|
||||
|
||||
def widget_schema(widget_type, extras=None):
|
||||
def widget_schema(widget_type: WidgetType, extras=None):
|
||||
"""
|
||||
Create a schema for a given widget type
|
||||
:param widget_type: The name of the widget
|
||||
|
@ -241,9 +321,9 @@ def widget_schema(widget_type, extras=None):
|
|||
:return:
|
||||
"""
|
||||
validator = container_schema(widget_type, extras=extras)
|
||||
if required := REQUIRED_COMPONENTS.get(widget_type):
|
||||
if required := widget_type.required_component:
|
||||
validator = cv.All(validator, requires_component(required))
|
||||
return cv.Exclusive(widget_type, df.CONF_WIDGETS), validator
|
||||
return cv.Exclusive(widget_type.name, df.CONF_WIDGETS), validator
|
||||
|
||||
|
||||
# All widget schemas must be defined before this is called.
|
||||
|
@ -257,4 +337,4 @@ def any_widget_schema(extras=None):
|
|||
:param extras: Additional schema to be applied to each generated one
|
||||
:return:
|
||||
"""
|
||||
return cv.Any(dict(widget_schema(wt, extras) for wt in WIDGET_PARTS))
|
||||
return cv.Any(dict(widget_schema(wt, extras) for wt in WIDGET_TYPES.values()))
|
||||
|
|
45
esphome/components/lvgl/touchscreens.py
Normal file
45
esphome/components/lvgl/touchscreens.py
Normal file
|
@ -0,0 +1,45 @@
|
|||
import esphome.codegen as cg
|
||||
from esphome.components.touchscreen import CONF_TOUCHSCREEN_ID, Touchscreen
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID
|
||||
from esphome.core import CORE
|
||||
|
||||
from .defines import (
|
||||
CONF_LONG_PRESS_REPEAT_TIME,
|
||||
CONF_LONG_PRESS_TIME,
|
||||
CONF_TOUCHSCREENS,
|
||||
)
|
||||
from .helpers import lvgl_components_required
|
||||
from .lvcode import lv
|
||||
from .schemas import PRESS_TIME
|
||||
from .types import LVTouchListener
|
||||
|
||||
CONF_TOUCHSCREEN = "touchscreen"
|
||||
TOUCHSCREENS_CONFIG = cv.maybe_simple_value(
|
||||
{
|
||||
cv.Required(CONF_TOUCHSCREEN_ID): cv.use_id(Touchscreen),
|
||||
cv.Optional(CONF_LONG_PRESS_TIME, default="400ms"): PRESS_TIME,
|
||||
cv.Optional(CONF_LONG_PRESS_REPEAT_TIME, default="100ms"): PRESS_TIME,
|
||||
cv.GenerateID(): cv.declare_id(LVTouchListener),
|
||||
},
|
||||
key=CONF_TOUCHSCREEN_ID,
|
||||
)
|
||||
|
||||
|
||||
def touchscreen_schema(config):
|
||||
value = cv.ensure_list(TOUCHSCREENS_CONFIG)(config)
|
||||
if value or CONF_TOUCHSCREEN not in CORE.loaded_integrations:
|
||||
return value
|
||||
return [TOUCHSCREENS_CONFIG(config)]
|
||||
|
||||
|
||||
async def touchscreens_to_code(var, config):
|
||||
for tconf in config.get(CONF_TOUCHSCREENS) or ():
|
||||
lvgl_components_required.add(CONF_TOUCHSCREEN)
|
||||
touchscreen = await cg.get_variable(tconf[CONF_TOUCHSCREEN_ID])
|
||||
lpt = tconf[CONF_LONG_PRESS_TIME].total_milliseconds
|
||||
lprt = tconf[CONF_LONG_PRESS_REPEAT_TIME].total_milliseconds
|
||||
listener = cg.new_Pvariable(tconf[CONF_ID], lpt, lprt)
|
||||
await cg.register_parented(listener, var)
|
||||
lv.indev_drv_register(listener.get_drv())
|
||||
cg.add(touchscreen.register_listener(listener))
|
61
esphome/components/lvgl/trigger.py
Normal file
61
esphome/components/lvgl/trigger.py
Normal file
|
@ -0,0 +1,61 @@
|
|||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.const import CONF_ID, CONF_ON_VALUE, CONF_TRIGGER_ID
|
||||
|
||||
from .defines import (
|
||||
CONF_ALIGN,
|
||||
CONF_ALIGN_TO,
|
||||
CONF_X,
|
||||
CONF_Y,
|
||||
LV_EVENT,
|
||||
LV_EVENT_TRIGGERS,
|
||||
literal,
|
||||
)
|
||||
from .lvcode import LambdaContext, add_line_marks, lv, lv_add
|
||||
from .widget import widget_map
|
||||
|
||||
lv_event_t_ptr = cg.global_ns.namespace("lv_event_t").operator("ptr")
|
||||
|
||||
|
||||
async def generate_triggers(lv_component):
|
||||
"""
|
||||
Generate LVGL triggers for all defined widgets
|
||||
Must be done after all widgets completed
|
||||
:param lv_component: The parent component
|
||||
:return:
|
||||
"""
|
||||
|
||||
for w in widget_map.values():
|
||||
if w.config:
|
||||
for event, conf in {
|
||||
event: conf
|
||||
for event, conf in w.config.items()
|
||||
if event in LV_EVENT_TRIGGERS
|
||||
}.items():
|
||||
conf = conf[0]
|
||||
w.add_flag("LV_OBJ_FLAG_CLICKABLE")
|
||||
event = "LV_EVENT_" + LV_EVENT[event[3:].upper()]
|
||||
await add_trigger(conf, event, lv_component, w)
|
||||
for conf in w.config.get(CONF_ON_VALUE, ()):
|
||||
await add_trigger(conf, "LV_EVENT_VALUE_CHANGED", lv_component, w)
|
||||
|
||||
# Generate align to directives while we're here
|
||||
if align_to := w.config.get(CONF_ALIGN_TO):
|
||||
target = widget_map[align_to[CONF_ID]].obj
|
||||
align = align_to[CONF_ALIGN]
|
||||
x = align_to[CONF_X]
|
||||
y = align_to[CONF_Y]
|
||||
lv.obj_align_to(w.obj, target, align, x, y)
|
||||
|
||||
|
||||
async def add_trigger(conf, event, lv_component, w):
|
||||
tid = conf[CONF_TRIGGER_ID]
|
||||
add_line_marks(tid)
|
||||
trigger = cg.new_Pvariable(tid)
|
||||
args = w.get_args()
|
||||
value = w.get_value()
|
||||
await automation.build_automation(trigger, args, conf)
|
||||
with LambdaContext([(lv_event_t_ptr, "event_data")]) as context:
|
||||
add_line_marks(tid)
|
||||
lv_add(trigger.trigger(value))
|
||||
lv_add(lv_component.add_event_cb(w.obj, await context.get_lambda(), literal(event)))
|
|
@ -1,27 +1,8 @@
|
|||
from esphome import codegen as cg
|
||||
from esphome import automation, codegen as cg
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObjClass
|
||||
|
||||
from .defines import CONF_LABEL, CONF_OBJ, CONF_TEXT
|
||||
|
||||
uint16_t_ptr = cg.uint16.operator("ptr")
|
||||
lvgl_ns = cg.esphome_ns.namespace("lvgl")
|
||||
char_ptr = cg.global_ns.namespace("char").operator("ptr")
|
||||
void_ptr = cg.void.operator("ptr")
|
||||
LvglComponent = lvgl_ns.class_("LvglComponent", cg.PollingComponent)
|
||||
lv_event_code_t = cg.global_ns.namespace("lv_event_code_t")
|
||||
FontEngine = lvgl_ns.class_("FontEngine")
|
||||
LvCompound = lvgl_ns.class_("LvCompound")
|
||||
lv_font_t = cg.global_ns.class_("lv_font_t")
|
||||
lv_style_t = cg.global_ns.struct("lv_style_t")
|
||||
lv_pseudo_button_t = lvgl_ns.class_("LvPseudoButton")
|
||||
lv_obj_base_t = cg.global_ns.class_("lv_obj_t", lv_pseudo_button_t)
|
||||
lv_obj_t_ptr = lv_obj_base_t.operator("ptr")
|
||||
lv_disp_t_ptr = cg.global_ns.struct("lv_disp_t").operator("ptr")
|
||||
lv_color_t = cg.global_ns.struct("lv_color_t")
|
||||
|
||||
|
||||
# this will be populated later, in __init__.py to avoid circular imports.
|
||||
WIDGET_TYPES: dict = {}
|
||||
from .defines import CONF_TEXT
|
||||
|
||||
|
||||
class LvType(cg.MockObjClass):
|
||||
|
@ -37,6 +18,38 @@ class LvType(cg.MockObjClass):
|
|||
return self.args[0][0] if len(self.args) else None
|
||||
|
||||
|
||||
uint16_t_ptr = cg.uint16.operator("ptr")
|
||||
lvgl_ns = cg.esphome_ns.namespace("lvgl")
|
||||
char_ptr = cg.global_ns.namespace("char").operator("ptr")
|
||||
void_ptr = cg.void.operator("ptr")
|
||||
LvglComponent = lvgl_ns.class_("LvglComponent", cg.PollingComponent)
|
||||
LvglComponentPtr = LvglComponent.operator("ptr")
|
||||
lv_event_code_t = cg.global_ns.namespace("lv_event_code_t")
|
||||
lv_indev_type_t = cg.global_ns.enum("lv_indev_type_t")
|
||||
FontEngine = lvgl_ns.class_("FontEngine")
|
||||
IdleTrigger = lvgl_ns.class_("IdleTrigger", automation.Trigger.template())
|
||||
ObjUpdateAction = lvgl_ns.class_("ObjUpdateAction", automation.Action)
|
||||
LvglCondition = lvgl_ns.class_("LvglCondition", automation.Condition)
|
||||
LvglAction = lvgl_ns.class_("LvglAction", automation.Action)
|
||||
LvCompound = lvgl_ns.class_("LvCompound")
|
||||
lv_font_t = cg.global_ns.class_("lv_font_t")
|
||||
lv_style_t = cg.global_ns.struct("lv_style_t")
|
||||
lv_pseudo_button_t = lvgl_ns.class_("LvPseudoButton")
|
||||
lv_obj_base_t = cg.global_ns.class_("lv_obj_t", lv_pseudo_button_t)
|
||||
lv_obj_t_ptr = lv_obj_base_t.operator("ptr")
|
||||
lv_disp_t_ptr = cg.global_ns.struct("lv_disp_t").operator("ptr")
|
||||
lv_color_t = cg.global_ns.struct("lv_color_t")
|
||||
lv_group_t = cg.global_ns.struct("lv_group_t")
|
||||
LVTouchListener = lvgl_ns.class_("LVTouchListener")
|
||||
LVEncoderListener = lvgl_ns.class_("LVEncoderListener")
|
||||
lv_obj_t = LvType("lv_obj_t")
|
||||
lv_img_t = LvType("lv_img_t")
|
||||
|
||||
|
||||
# this will be populated later, in __init__.py to avoid circular imports.
|
||||
WIDGET_TYPES: dict = {}
|
||||
|
||||
|
||||
class LvText(LvType):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(
|
||||
|
@ -48,17 +61,77 @@ class LvText(LvType):
|
|||
self.value_property = CONF_TEXT
|
||||
|
||||
|
||||
lv_obj_t = LvType("lv_obj_t")
|
||||
lv_label_t = LvText("lv_label_t")
|
||||
|
||||
LV_TYPES = {
|
||||
CONF_LABEL: lv_label_t,
|
||||
CONF_OBJ: lv_obj_t,
|
||||
}
|
||||
|
||||
|
||||
def get_widget_type(typestr: str) -> LvType:
|
||||
return LV_TYPES[typestr]
|
||||
class LvBoolean(LvType):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(
|
||||
*args,
|
||||
largs=[(cg.bool_, "x")],
|
||||
lvalue=lambda w: w.has_state("LV_STATE_CHECKED"),
|
||||
has_on_value=True,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
CUSTOM_EVENT = ID("lv_custom_event", False, type=lv_event_code_t)
|
||||
|
||||
|
||||
class WidgetType:
|
||||
"""
|
||||
Describes a type of Widget, e.g. "bar" or "line"
|
||||
"""
|
||||
|
||||
def __init__(self, name, w_type, parts, schema=None, modify_schema=None):
|
||||
"""
|
||||
:param name: The widget name, e.g. "bar"
|
||||
:param w_type: The C type of the widget
|
||||
:param parts: What parts this widget supports
|
||||
:param schema: The config schema for defining a widget
|
||||
:param modify_schema: A schema to update the widget
|
||||
"""
|
||||
self.name = name
|
||||
self.w_type = w_type
|
||||
self.parts = parts
|
||||
if schema is None:
|
||||
self.schema = {}
|
||||
else:
|
||||
self.schema = schema
|
||||
if modify_schema is None:
|
||||
self.modify_schema = self.schema
|
||||
else:
|
||||
self.modify_schema = self.schema
|
||||
|
||||
@property
|
||||
def animated(self):
|
||||
return False
|
||||
|
||||
@property
|
||||
def required_component(self):
|
||||
return None
|
||||
|
||||
def is_compound(self):
|
||||
return self.w_type.inherits_from(LvCompound)
|
||||
|
||||
async def to_code(self, w, config: dict):
|
||||
"""
|
||||
Generate code for a given widget
|
||||
:param w: The widget
|
||||
:param config: Its configuration
|
||||
:return: Generated code as a list of text lines
|
||||
"""
|
||||
raise NotImplementedError(f"No to_code defined for {self.name}")
|
||||
|
||||
def obj_creator(self, parent: MockObjClass, config: dict):
|
||||
"""
|
||||
Create an instance of the widget type
|
||||
:param parent: The parent to which it should be attached
|
||||
:param config: Its configuration
|
||||
:return: Generated code as a single text line
|
||||
"""
|
||||
return f"lv_{self.name}_create({parent})"
|
||||
|
||||
def get_uses(self):
|
||||
"""
|
||||
Get a list of other widgets used by this one
|
||||
:return:
|
||||
"""
|
||||
return ()
|
||||
|
|
|
@ -4,9 +4,9 @@ from typing import Any
|
|||
from esphome import codegen as cg, config_validation as cv
|
||||
from esphome.config_validation import Invalid
|
||||
from esphome.const import CONF_GROUP, CONF_ID, CONF_STATE
|
||||
from esphome.core import ID, TimePeriod
|
||||
from esphome.core import CORE, TimePeriod
|
||||
from esphome.coroutine import FakeAwaitable
|
||||
from esphome.cpp_generator import MockObjClass
|
||||
from esphome.cpp_generator import MockObj, MockObjClass, VariableDeclarationExpression
|
||||
|
||||
from .defines import (
|
||||
CONF_DEFAULT,
|
||||
|
@ -16,83 +16,26 @@ from .defines import (
|
|||
OBJ_FLAGS,
|
||||
PARTS,
|
||||
STATES,
|
||||
ConstantLiteral,
|
||||
LValidator,
|
||||
join_enums,
|
||||
literal,
|
||||
)
|
||||
from .helpers import add_lv_use
|
||||
from .lvcode import ConstantLiteral, add_line_marks, lv, lv_add, lv_assign, lv_obj
|
||||
from .schemas import ALL_STYLES
|
||||
from .types import WIDGET_TYPES, LvCompound, lv_obj_t
|
||||
from .lvcode import add_group, add_line_marks, lv, lv_add, lv_assign, lv_expr, lv_obj
|
||||
from .schemas import ALL_STYLES, STYLE_REMAP
|
||||
from .types import WIDGET_TYPES, LvType, WidgetType, lv_obj_t, lv_obj_t_ptr
|
||||
|
||||
EVENT_LAMB = "event_lamb__"
|
||||
|
||||
|
||||
class WidgetType:
|
||||
"""
|
||||
Describes a type of Widget, e.g. "bar" or "line"
|
||||
"""
|
||||
|
||||
def __init__(self, name, schema=None, modify_schema=None):
|
||||
"""
|
||||
:param name: The widget name, e.g. "bar"
|
||||
:param schema: The config schema for defining a widget
|
||||
:param modify_schema: A schema to update the widget
|
||||
"""
|
||||
self.name = name
|
||||
self.schema = schema or {}
|
||||
if modify_schema is None:
|
||||
self.modify_schema = schema
|
||||
else:
|
||||
self.modify_schema = modify_schema
|
||||
|
||||
@property
|
||||
def animated(self):
|
||||
return False
|
||||
|
||||
@property
|
||||
def w_type(self):
|
||||
"""
|
||||
Get the type associated with this widget
|
||||
:return:
|
||||
"""
|
||||
return lv_obj_t
|
||||
|
||||
def is_compound(self):
|
||||
return self.w_type.inherits_from(LvCompound)
|
||||
|
||||
async def to_code(self, w, config: dict):
|
||||
"""
|
||||
Generate code for a given widget
|
||||
:param w: The widget
|
||||
:param config: Its configuration
|
||||
:return: Generated code as a list of text lines
|
||||
"""
|
||||
raise NotImplementedError(f"No to_code defined for {self.name}")
|
||||
|
||||
def obj_creator(self, parent: MockObjClass, config: dict):
|
||||
"""
|
||||
Create an instance of the widget type
|
||||
:param parent: The parent to which it should be attached
|
||||
:param config: Its configuration
|
||||
:return: Generated code as a single text line
|
||||
"""
|
||||
return f"lv_{self.name}_create({parent})"
|
||||
|
||||
def get_uses(self):
|
||||
"""
|
||||
Get a list of other widgets used by this one
|
||||
:return:
|
||||
"""
|
||||
return ()
|
||||
|
||||
|
||||
class LvScrActType(WidgetType):
|
||||
"""
|
||||
A "widget" representing the active screen.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("lv_scr_act()")
|
||||
super().__init__("lv_scr_act()", lv_obj_t, ())
|
||||
|
||||
def obj_creator(self, parent: MockObjClass, config: dict):
|
||||
return []
|
||||
|
@ -135,17 +78,20 @@ class Widget:
|
|||
return f"{self.var}->obj"
|
||||
return self.var
|
||||
|
||||
def add_state(self, *args):
|
||||
return lv_obj.add_state(self.obj, *args)
|
||||
def add_state(self, state):
|
||||
return lv_obj.add_state(self.obj, literal(state))
|
||||
|
||||
def clear_state(self, *args):
|
||||
return lv_obj.clear_state(self.obj, *args)
|
||||
def clear_state(self, state):
|
||||
return lv_obj.clear_state(self.obj, literal(state))
|
||||
|
||||
def add_flag(self, *args):
|
||||
return lv_obj.add_flag(self.obj, *args)
|
||||
def has_state(self, state):
|
||||
return lv_expr.obj_get_state(self.obj) & literal(state) != 0
|
||||
|
||||
def clear_flag(self, *args):
|
||||
return lv_obj.clear_flag(self.obj, *args)
|
||||
def add_flag(self, flag):
|
||||
return lv_obj.add_flag(self.obj, literal(flag))
|
||||
|
||||
def clear_flag(self, flag):
|
||||
return lv_obj.clear_flag(self.obj, literal(flag))
|
||||
|
||||
def set_property(self, prop, value, animated: bool = None, ltype=None):
|
||||
if isinstance(value, dict):
|
||||
|
@ -184,6 +130,16 @@ class Widget:
|
|||
def __str__(self):
|
||||
return f"({self.var}, {self.type})"
|
||||
|
||||
def get_args(self):
|
||||
if isinstance(self.type.w_type, LvType):
|
||||
return self.type.w_type.args
|
||||
return [(lv_obj_t_ptr, "obj")]
|
||||
|
||||
def get_value(self):
|
||||
if isinstance(self.type.w_type, LvType):
|
||||
return self.type.w_type.value(self)
|
||||
return self.obj
|
||||
|
||||
|
||||
# Map of widgets to their config, used for trigger generation
|
||||
widget_map: dict[Any, Widget] = {}
|
||||
|
@ -205,7 +161,8 @@ def get_widget_generator(wid):
|
|||
yield
|
||||
|
||||
|
||||
async def get_widget(wid: ID) -> Widget:
|
||||
async def get_widget(config: dict, id: str = CONF_ID) -> Widget:
|
||||
wid = config[id]
|
||||
if obj := widget_map.get(wid):
|
||||
return obj
|
||||
return await FakeAwaitable(get_widget_generator(wid))
|
||||
|
@ -263,7 +220,10 @@ async def set_obj_properties(w: Widget, config):
|
|||
}.items():
|
||||
if isinstance(ALL_STYLES[prop], LValidator):
|
||||
value = await ALL_STYLES[prop].process(value)
|
||||
w.set_style(prop, value, lv_state)
|
||||
prop_r = STYLE_REMAP.get(prop, prop)
|
||||
w.set_style(prop_r, value, lv_state)
|
||||
if group := add_group(config.get(CONF_GROUP)):
|
||||
lv.group_add_obj(group, w.obj)
|
||||
flag_clr = set()
|
||||
flag_set = set()
|
||||
props = parts[CONF_MAIN][CONF_DEFAULT]
|
||||
|
@ -291,14 +251,14 @@ async def set_obj_properties(w: Widget, config):
|
|||
else:
|
||||
clears.add(key)
|
||||
if adds:
|
||||
adds = ConstantLiteral(join_enums(adds, "LV_STATE_"))
|
||||
adds = join_enums(adds, "LV_STATE_")
|
||||
w.add_state(adds)
|
||||
if clears:
|
||||
clears = ConstantLiteral(join_enums(clears, "LV_STATE_"))
|
||||
clears = join_enums(clears, "LV_STATE_")
|
||||
w.clear_state(clears)
|
||||
for key, value in lambs.items():
|
||||
lamb = await cg.process_lambda(value, [], return_type=cg.bool_)
|
||||
state = ConstantLiteral(f"LV_STATE_{key.upper}")
|
||||
state = f"LV_STATE_{key.upper}"
|
||||
lv.cond_if(lamb)
|
||||
w.add_state(state)
|
||||
lv.cond_else()
|
||||
|
@ -338,10 +298,19 @@ async def widget_to_code(w_cnfig, w_type, parent):
|
|||
var = cg.new_Pvariable(wid)
|
||||
lv_add(var.set_obj(creator))
|
||||
else:
|
||||
var = cg.Pvariable(wid, cg.nullptr, type_=lv_obj_t)
|
||||
var = MockObj(wid, "->")
|
||||
decl = VariableDeclarationExpression(lv_obj_t, "*", wid)
|
||||
CORE.add_global(decl)
|
||||
CORE.register_variable(wid, var)
|
||||
lv_assign(var, creator)
|
||||
|
||||
widget = Widget.create(wid, var, spec, w_cnfig, parent)
|
||||
await set_obj_properties(widget, w_cnfig)
|
||||
await add_widgets(widget, w_cnfig)
|
||||
await spec.to_code(widget, w_cnfig)
|
||||
|
||||
|
||||
lv_scr_act_spec = LvScrActType()
|
||||
lv_scr_act = Widget.create(
|
||||
None, ConstantLiteral("lv_scr_act()"), lv_scr_act_spec, {}, parent=None
|
||||
)
|
||||
|
|
|
@ -8,6 +8,7 @@ static const char *const TAG = "matrix_keypad";
|
|||
|
||||
void MatrixKeypad::setup() {
|
||||
for (auto *pin : this->rows_) {
|
||||
pin->setup();
|
||||
if (!has_diodes_) {
|
||||
pin->pin_mode(gpio::FLAG_INPUT);
|
||||
} else {
|
||||
|
@ -15,6 +16,7 @@ void MatrixKeypad::setup() {
|
|||
}
|
||||
}
|
||||
for (auto *pin : this->columns_) {
|
||||
pin->setup();
|
||||
if (has_pulldowns_) {
|
||||
pin->pin_mode(gpio::FLAG_INPUT);
|
||||
} else {
|
||||
|
|
|
@ -148,7 +148,7 @@ WakeWordModel::WakeWordModel(const uint8_t *model_start, float probability_cutof
|
|||
};
|
||||
|
||||
bool WakeWordModel::determine_detected() {
|
||||
int32_t sum = 0;
|
||||
uint32_t sum = 0;
|
||||
for (auto &prob : this->recent_streaming_probabilities_) {
|
||||
sum += prob;
|
||||
}
|
||||
|
@ -175,12 +175,14 @@ VADModel::VADModel(const uint8_t *model_start, float probability_cutoff, size_t
|
|||
};
|
||||
|
||||
bool VADModel::determine_detected() {
|
||||
uint8_t max = 0;
|
||||
uint32_t sum = 0;
|
||||
for (auto &prob : this->recent_streaming_probabilities_) {
|
||||
max = std::max(prob, max);
|
||||
sum += prob;
|
||||
}
|
||||
|
||||
return max > this->probability_cutoff_;
|
||||
float sliding_window_average = static_cast<float>(sum) / static_cast<float>(255 * this->sliding_window_size_);
|
||||
|
||||
return sliding_window_average > this->probability_cutoff_;
|
||||
}
|
||||
|
||||
} // namespace micro_wake_word
|
||||
|
|
|
@ -110,7 +110,7 @@ void MitsubishiClimate::transmit_state() {
|
|||
// Byte 15: HVAC specfic, i.e. POWERFUL, SMART SET, PLASMA, always 0x00
|
||||
// Byte 16: Constant 0x00
|
||||
// Byte 17: Checksum: SUM[Byte0...Byte16]
|
||||
uint8_t remote_state[18] = {0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x08, 0x00, 0x00,
|
||||
uint8_t remote_state[18] = {0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
switch (this->mode) {
|
||||
|
@ -136,6 +136,12 @@ void MitsubishiClimate::transmit_state() {
|
|||
break;
|
||||
case climate::CLIMATE_MODE_OFF:
|
||||
default:
|
||||
remote_state[6] = MITSUBISHI_MODE_COOL;
|
||||
remote_state[8] = MITSUBISHI_MODE_A_COOL;
|
||||
if (this->supports_heat_) {
|
||||
remote_state[6] = MITSUBISHI_MODE_HEAT;
|
||||
remote_state[8] = MITSUBISHI_MODE_A_HEAT;
|
||||
}
|
||||
remote_state[5] = MITSUBISHI_OFF;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
from esphome.const import (
|
||||
CONF_ESPHOME,
|
||||
CONF_ON_ERROR,
|
||||
CONF_OTA,
|
||||
CONF_PLATFORM,
|
||||
CONF_TRIGGER_ID,
|
||||
)
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
|
||||
from esphome.const import CONF_ESPHOME, CONF_OTA, CONF_PLATFORM, CONF_TRIGGER_ID
|
||||
|
||||
CODEOWNERS = ["@esphome/core"]
|
||||
AUTO_LOAD = ["md5", "safe_mode"]
|
||||
|
||||
|
@ -13,7 +18,6 @@ IS_PLATFORM_COMPONENT = True
|
|||
CONF_ON_ABORT = "on_abort"
|
||||
CONF_ON_BEGIN = "on_begin"
|
||||
CONF_ON_END = "on_end"
|
||||
CONF_ON_ERROR = "on_error"
|
||||
CONF_ON_PROGRESS = "on_progress"
|
||||
CONF_ON_STATE_CHANGE = "on_state_change"
|
||||
|
||||
|
|
|
@ -44,6 +44,8 @@ class PIDClimate : public climate::Climate, public Component {
|
|||
float get_kp() { return controller_.kp_; }
|
||||
float get_ki() { return controller_.ki_; }
|
||||
float get_kd() { return controller_.kd_; }
|
||||
float get_min_integral() { return controller_.min_integral_; }
|
||||
float get_max_integral() { return controller_.max_integral_; }
|
||||
float get_proportional_term() const { return controller_.proportional_term_; }
|
||||
float get_integral_term() const { return controller_.integral_term_; }
|
||||
float get_derivative_term() const { return controller_.derivative_term_; }
|
||||
|
|
|
@ -17,6 +17,7 @@ from esphome.const import (
|
|||
CONF_ICON,
|
||||
CONF_ID,
|
||||
CONF_IGNORE_OUT_OF_RANGE,
|
||||
CONF_MULTIPLE,
|
||||
CONF_ON_RAW_VALUE,
|
||||
CONF_ON_VALUE,
|
||||
CONF_ON_VALUE_RANGE,
|
||||
|
@ -249,6 +250,7 @@ CalibratePolynomialFilter = sensor_ns.class_("CalibratePolynomialFilter", Filter
|
|||
SensorInRangeCondition = sensor_ns.class_("SensorInRangeCondition", Filter)
|
||||
ClampFilter = sensor_ns.class_("ClampFilter", Filter)
|
||||
RoundFilter = sensor_ns.class_("RoundFilter", Filter)
|
||||
RoundMultipleFilter = sensor_ns.class_("RoundMultipleFilter", Filter)
|
||||
|
||||
validate_unit_of_measurement = cv.string_strict
|
||||
validate_accuracy_decimals = cv.int_
|
||||
|
@ -734,6 +736,23 @@ async def round_filter_to_code(config, filter_id):
|
|||
)
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register(
|
||||
"round_to_multiple_of",
|
||||
RoundMultipleFilter,
|
||||
cv.maybe_simple_value(
|
||||
{
|
||||
cv.Required(CONF_MULTIPLE): cv.positive_not_null_float,
|
||||
},
|
||||
key=CONF_MULTIPLE,
|
||||
),
|
||||
)
|
||||
async def round_multiple_filter_to_code(config, filter_id):
|
||||
return cg.new_Pvariable(
|
||||
filter_id,
|
||||
config[CONF_MULTIPLE],
|
||||
)
|
||||
|
||||
|
||||
async def build_filters(config):
|
||||
return await cg.build_registry_list(FILTER_REGISTRY, config)
|
||||
|
||||
|
|
|
@ -472,5 +472,13 @@ optional<float> RoundFilter::new_value(float value) {
|
|||
return value;
|
||||
}
|
||||
|
||||
RoundMultipleFilter::RoundMultipleFilter(float multiple) : multiple_(multiple) {}
|
||||
optional<float> RoundMultipleFilter::new_value(float value) {
|
||||
if (std::isfinite(value)) {
|
||||
return value - remainderf(value, this->multiple_);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
} // namespace sensor
|
||||
} // namespace esphome
|
||||
|
|
|
@ -431,5 +431,14 @@ class RoundFilter : public Filter {
|
|||
uint8_t precision_;
|
||||
};
|
||||
|
||||
class RoundMultipleFilter : public Filter {
|
||||
public:
|
||||
explicit RoundMultipleFilter(float multiple);
|
||||
optional<float> new_value(float value) override;
|
||||
|
||||
protected:
|
||||
float multiple_;
|
||||
};
|
||||
|
||||
} // namespace sensor
|
||||
} // namespace esphome
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
|
||||
from esphome.components import binary_sensor, display
|
||||
from esphome.const import CONF_PAGE_ID
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_PAGE_ID, CONF_PAGES
|
||||
|
||||
from .. import touchscreen_ns, CONF_TOUCHSCREEN_ID, Touchscreen, TouchListener
|
||||
from .. import CONF_TOUCHSCREEN_ID, TouchListener, Touchscreen, touchscreen_ns
|
||||
|
||||
DEPENDENCIES = ["touchscreen"]
|
||||
|
||||
|
@ -22,7 +21,7 @@ CONF_Y_MIN = "y_min"
|
|||
CONF_Y_MAX = "y_max"
|
||||
|
||||
|
||||
def validate_coords(config):
|
||||
def _validate_coords(config):
|
||||
if (
|
||||
config[CONF_X_MAX] < config[CONF_X_MIN]
|
||||
or config[CONF_Y_MAX] < config[CONF_Y_MIN]
|
||||
|
@ -33,6 +32,15 @@ def validate_coords(config):
|
|||
return config
|
||||
|
||||
|
||||
def _set_pages(config: dict) -> dict:
|
||||
if CONF_PAGES in config or CONF_PAGE_ID not in config:
|
||||
return config
|
||||
|
||||
config = config.copy()
|
||||
config[CONF_PAGES] = [config.pop(CONF_PAGE_ID)]
|
||||
return config
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
binary_sensor.binary_sensor_schema(TouchscreenBinarySensor)
|
||||
.extend(
|
||||
|
@ -42,11 +50,17 @@ CONFIG_SCHEMA = cv.All(
|
|||
cv.Required(CONF_X_MAX): cv.int_range(min=0, max=2000),
|
||||
cv.Required(CONF_Y_MIN): cv.int_range(min=0, max=2000),
|
||||
cv.Required(CONF_Y_MAX): cv.int_range(min=0, max=2000),
|
||||
cv.Optional(CONF_PAGE_ID): cv.use_id(display.DisplayPage),
|
||||
cv.Exclusive(CONF_PAGE_ID, group_of_exclusion=CONF_PAGES): cv.use_id(
|
||||
display.DisplayPage
|
||||
),
|
||||
cv.Exclusive(CONF_PAGES, group_of_exclusion=CONF_PAGES): cv.ensure_list(
|
||||
cv.use_id(display.DisplayPage)
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA),
|
||||
validate_coords,
|
||||
_validate_coords,
|
||||
_set_pages,
|
||||
)
|
||||
|
||||
|
||||
|
@ -64,6 +78,6 @@ async def to_code(config):
|
|||
)
|
||||
)
|
||||
|
||||
if CONF_PAGE_ID in config:
|
||||
page = await cg.get_variable(config[CONF_PAGE_ID])
|
||||
cg.add(var.set_page(page))
|
||||
for page_id in config.get(CONF_PAGES, []):
|
||||
page = await cg.get_variable(page_id)
|
||||
cg.add(var.add_page(page))
|
||||
|
|
|
@ -11,8 +11,9 @@ void TouchscreenBinarySensor::setup() {
|
|||
void TouchscreenBinarySensor::touch(TouchPoint tp) {
|
||||
bool touched = (tp.x >= this->x_min_ && tp.x <= this->x_max_ && tp.y >= this->y_min_ && tp.y <= this->y_max_);
|
||||
|
||||
if (this->page_ != nullptr) {
|
||||
touched &= this->page_ == this->parent_->get_display()->get_active_page();
|
||||
if (!this->pages_.empty()) {
|
||||
auto *current_page = this->parent_->get_display()->get_active_page();
|
||||
touched &= std::find(this->pages_.begin(), this->pages_.end(), current_page) != this->pages_.end();
|
||||
}
|
||||
if (touched) {
|
||||
this->publish_state(true);
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace esphome {
|
||||
namespace touchscreen {
|
||||
|
||||
|
@ -30,14 +32,14 @@ class TouchscreenBinarySensor : public binary_sensor::BinarySensor,
|
|||
int16_t get_width() { return this->x_max_ - this->x_min_; }
|
||||
int16_t get_height() { return this->y_max_ - this->y_min_; }
|
||||
|
||||
void set_page(display::DisplayPage *page) { this->page_ = page; }
|
||||
void add_page(display::DisplayPage *page) { this->pages_.push_back(page); }
|
||||
|
||||
void touch(TouchPoint tp) override;
|
||||
void release() override;
|
||||
|
||||
protected:
|
||||
int16_t x_min_, x_max_, y_min_, y_max_;
|
||||
display::DisplayPage *page_{nullptr};
|
||||
std::vector<display::DisplayPage *> pages_{};
|
||||
};
|
||||
|
||||
} // namespace touchscreen
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import mqtt, web_server
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
from esphome.const import (
|
||||
CONF_DEVICE_CLASS,
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_FORCE_UPDATE,
|
||||
CONF_ID,
|
||||
CONF_MQTT_ID,
|
||||
CONF_WEB_SERVER_ID,
|
||||
|
@ -23,8 +24,12 @@ UpdateEntity = update_ns.class_("UpdateEntity", cg.EntityBase)
|
|||
|
||||
UpdateInfo = update_ns.struct("UpdateInfo")
|
||||
|
||||
PerformAction = update_ns.class_("PerformAction", automation.Action)
|
||||
IsAvailableCondition = update_ns.class_("IsAvailableCondition", automation.Condition)
|
||||
PerformAction = update_ns.class_(
|
||||
"PerformAction", automation.Action, cg.Parented.template(UpdateEntity)
|
||||
)
|
||||
IsAvailableCondition = update_ns.class_(
|
||||
"IsAvailableCondition", automation.Condition, cg.Parented.template(UpdateEntity)
|
||||
)
|
||||
|
||||
DEVICE_CLASSES = [
|
||||
DEVICE_CLASS_EMPTY,
|
||||
|
@ -92,24 +97,37 @@ async def to_code(config):
|
|||
cg.add_global(update_ns.using)
|
||||
|
||||
|
||||
UPDATE_AUTOMATION_SCHEMA = cv.Schema(
|
||||
@automation.register_action(
|
||||
"update.perform",
|
||||
PerformAction,
|
||||
automation.maybe_simple_id(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(UpdateEntity),
|
||||
cv.Optional(CONF_FORCE_UPDATE, default=False): cv.templatable(cv.boolean),
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action("update.perform", PerformAction, UPDATE_AUTOMATION_SCHEMA)
|
||||
async def update_perform_action_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, paren, paren)
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
|
||||
force = await cg.templatable(config[CONF_FORCE_UPDATE], args, cg.bool_)
|
||||
cg.add(var.set_force(force))
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_condition(
|
||||
"update.is_available", IsAvailableCondition, UPDATE_AUTOMATION_SCHEMA
|
||||
"update.is_available",
|
||||
IsAvailableCondition,
|
||||
automation.maybe_simple_id(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(UpdateEntity),
|
||||
}
|
||||
),
|
||||
)
|
||||
async def update_is_available_condition_to_code(
|
||||
config, condition_id, template_arg, args
|
||||
):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(condition_id, paren, paren)
|
||||
var = cg.new_Pvariable(condition_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
return var
|
||||
|
|
23
esphome/components/update/automation.h
Normal file
23
esphome/components/update/automation.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include "update_entity.h"
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace update {
|
||||
|
||||
template<typename... Ts> class PerformAction : public Action<Ts...>, public Parented<UpdateEntity> {
|
||||
TEMPLATABLE_VALUE(bool, force)
|
||||
|
||||
public:
|
||||
void play(Ts... x) override { this->parent_->perform(this->force_.value(x...)); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class IsAvailableCondition : public Condition<Ts...>, public Parented<UpdateEntity> {
|
||||
public:
|
||||
bool check(Ts... x) override { return this->parent_->state == UPDATE_STATE_AVAILABLE; }
|
||||
};
|
||||
|
||||
} // namespace update
|
||||
} // namespace esphome
|
|
@ -32,7 +32,9 @@ class UpdateEntity : public EntityBase, public EntityBase_DeviceClass {
|
|||
|
||||
void publish_state();
|
||||
|
||||
virtual void perform() = 0;
|
||||
void perform() { this->perform(false); }
|
||||
|
||||
virtual void perform(bool force) = 0;
|
||||
|
||||
const UpdateInfo &update_info = update_info_;
|
||||
const UpdateState &state = state_;
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_MICROPHONE,
|
||||
CONF_SPEAKER,
|
||||
CONF_MEDIA_PLAYER,
|
||||
CONF_ON_CLIENT_CONNECTED,
|
||||
CONF_ON_CLIENT_DISCONNECTED,
|
||||
CONF_ON_IDLE,
|
||||
)
|
||||
from esphome import automation
|
||||
from esphome.automation import register_action, register_condition
|
||||
from esphome.components import microphone, speaker, media_player
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import media_player, microphone, speaker
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_MEDIA_PLAYER,
|
||||
CONF_MICROPHONE,
|
||||
CONF_ON_CLIENT_CONNECTED,
|
||||
CONF_ON_CLIENT_DISCONNECTED,
|
||||
CONF_ON_ERROR,
|
||||
CONF_ON_IDLE,
|
||||
CONF_SPEAKER,
|
||||
)
|
||||
|
||||
AUTO_LOAD = ["socket"]
|
||||
DEPENDENCIES = ["api", "microphone"]
|
||||
|
@ -20,7 +20,6 @@ DEPENDENCIES = ["api", "microphone"]
|
|||
CODEOWNERS = ["@jesserockz"]
|
||||
|
||||
CONF_ON_END = "on_end"
|
||||
CONF_ON_ERROR = "on_error"
|
||||
CONF_ON_INTENT_END = "on_intent_end"
|
||||
CONF_ON_INTENT_START = "on_intent_start"
|
||||
CONF_ON_LISTENING = "on_listening"
|
||||
|
|
1
esphome/components/watchdog/__init__.py
Normal file
1
esphome/components/watchdog/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
CODEOWNERS = ["@oarcher"]
|
|
@ -15,7 +15,6 @@
|
|||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace http_request {
|
||||
namespace watchdog {
|
||||
|
||||
static const char *const TAG = "http_request.watchdog";
|
||||
|
@ -72,5 +71,4 @@ uint32_t WatchdogManager::get_timeout_() {
|
|||
}
|
||||
|
||||
} // namespace watchdog
|
||||
} // namespace http_request
|
||||
} // namespace esphome
|
|
@ -5,7 +5,6 @@
|
|||
#include <cstdint>
|
||||
|
||||
namespace esphome {
|
||||
namespace http_request {
|
||||
namespace watchdog {
|
||||
|
||||
class WatchdogManager {
|
||||
|
@ -22,5 +21,4 @@ class WatchdogManager {
|
|||
};
|
||||
|
||||
} // namespace watchdog
|
||||
} // namespace http_request
|
||||
} // namespace esphome
|
|
@ -1,15 +1,19 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
import esphome.final_validate as fv
|
||||
from esphome import automation
|
||||
from esphome.automation import Condition
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.esp32 import add_idf_sdkconfig_option, const, get_esp32_variant
|
||||
from esphome.components.network import IPAddress
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_AP,
|
||||
CONF_BSSID,
|
||||
CONF_CERTIFICATE,
|
||||
CONF_CERTIFICATE_AUTHORITY,
|
||||
CONF_CHANNEL,
|
||||
CONF_DNS1,
|
||||
CONF_DNS2,
|
||||
CONF_DOMAIN,
|
||||
CONF_EAP,
|
||||
CONF_ENABLE_BTM,
|
||||
CONF_ENABLE_ON_BOOT,
|
||||
CONF_ENABLE_RRM,
|
||||
|
@ -17,29 +21,26 @@ from esphome.const import (
|
|||
CONF_GATEWAY,
|
||||
CONF_HIDDEN,
|
||||
CONF_ID,
|
||||
CONF_IDENTITY,
|
||||
CONF_KEY,
|
||||
CONF_MANUAL_IP,
|
||||
CONF_NETWORKS,
|
||||
CONF_ON_CONNECT,
|
||||
CONF_ON_DISCONNECT,
|
||||
CONF_PASSWORD,
|
||||
CONF_POWER_SAVE_MODE,
|
||||
CONF_PRIORITY,
|
||||
CONF_REBOOT_TIMEOUT,
|
||||
CONF_SSID,
|
||||
CONF_STATIC_IP,
|
||||
CONF_SUBNET,
|
||||
CONF_USE_ADDRESS,
|
||||
CONF_PRIORITY,
|
||||
CONF_IDENTITY,
|
||||
CONF_CERTIFICATE_AUTHORITY,
|
||||
CONF_CERTIFICATE,
|
||||
CONF_KEY,
|
||||
CONF_USERNAME,
|
||||
CONF_EAP,
|
||||
CONF_TTLS_PHASE_2,
|
||||
CONF_ON_CONNECT,
|
||||
CONF_ON_DISCONNECT,
|
||||
CONF_USE_ADDRESS,
|
||||
CONF_USERNAME,
|
||||
)
|
||||
from esphome.core import CORE, HexInt, coroutine_with_priority
|
||||
from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant, const
|
||||
from esphome.components.network import IPAddress
|
||||
import esphome.final_validate as fv
|
||||
|
||||
from . import wpa2_eap
|
||||
|
||||
AUTO_LOAD = ["network"]
|
||||
|
|
|
@ -7,16 +7,15 @@ so that it doesn't crash if it's not installed.
|
|||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
from esphome.core import CORE
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_USERNAME,
|
||||
CONF_IDENTITY,
|
||||
CONF_PASSWORD,
|
||||
CONF_CERTIFICATE,
|
||||
CONF_IDENTITY,
|
||||
CONF_KEY,
|
||||
CONF_PASSWORD,
|
||||
CONF_USERNAME,
|
||||
)
|
||||
|
||||
from esphome.core import CORE
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -49,8 +48,8 @@ def wrapped_load_pem_x509_certificate(value):
|
|||
def wrapped_load_pem_private_key(value, password):
|
||||
validate_cryptography_installed()
|
||||
|
||||
from cryptography.hazmat.primitives.serialization import load_pem_private_key
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives.serialization import load_pem_private_key
|
||||
|
||||
if password:
|
||||
password = password.encode("UTF-8")
|
||||
|
@ -91,7 +90,7 @@ def _validate_load_private_key(key, cert_pw):
|
|||
|
||||
|
||||
def _check_private_key_cert_match(key, cert):
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa, ec
|
||||
from cryptography.hazmat.primitives.asymmetric import ec, rsa
|
||||
|
||||
def check_match_a():
|
||||
return key.public_key().public_numbers() == cert.public_key().public_numbers()
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import text_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_BSSID,
|
||||
CONF_DNS_ADDRESS,
|
||||
CONF_IP_ADDRESS,
|
||||
CONF_MAC_ADDRESS,
|
||||
CONF_SCAN_RESULTS,
|
||||
CONF_SSID,
|
||||
CONF_MAC_ADDRESS,
|
||||
CONF_DNS_ADDRESS,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
DEVICE_CLASS_SIGNAL_STRENGTH,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
|
|
|
@ -1,40 +1,38 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import abc
|
||||
from contextlib import contextmanager
|
||||
import contextvars
|
||||
import functools
|
||||
import heapq
|
||||
import logging
|
||||
import re
|
||||
|
||||
from typing import Union, Any
|
||||
|
||||
from contextlib import contextmanager
|
||||
import contextvars
|
||||
from typing import Any, Union
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from esphome import core, yaml_util, loader, pins
|
||||
import esphome.core.config as core_config
|
||||
from esphome import core, loader, pins, yaml_util
|
||||
from esphome.config_helpers import Extend, Remove
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ESPHOME,
|
||||
CONF_ID,
|
||||
CONF_PLATFORM,
|
||||
CONF_PACKAGES,
|
||||
CONF_SUBSTITUTIONS,
|
||||
CONF_EXTERNAL_COMPONENTS,
|
||||
CONF_ID,
|
||||
CONF_PACKAGES,
|
||||
CONF_PLATFORM,
|
||||
CONF_SUBSTITUTIONS,
|
||||
TARGET_PLATFORMS,
|
||||
)
|
||||
from esphome.core import CORE, EsphomeError, DocumentRange
|
||||
from esphome.helpers import indent
|
||||
from esphome.util import safe_print, OrderedDict
|
||||
|
||||
from esphome.config_helpers import Extend, Remove
|
||||
from esphome.loader import get_component, get_platform, ComponentManifest
|
||||
from esphome.yaml_util import is_secret, ESPHomeDataBase, ESPForceValue
|
||||
from esphome.voluptuous_schema import ExtraKeysInvalid
|
||||
from esphome.log import color, Fore
|
||||
from esphome.core import CORE, DocumentRange, EsphomeError
|
||||
import esphome.core.config as core_config
|
||||
import esphome.final_validate as fv
|
||||
import esphome.config_validation as cv
|
||||
from esphome.types import ConfigType, ConfigFragmentType
|
||||
from esphome.helpers import indent
|
||||
from esphome.loader import ComponentManifest, get_component, get_platform
|
||||
from esphome.log import Fore, color
|
||||
from esphome.types import ConfigFragmentType, ConfigType
|
||||
from esphome.util import OrderedDict, safe_print
|
||||
from esphome.voluptuous_schema import ExtraKeysInvalid
|
||||
from esphome.yaml_util import ESPForceValue, ESPHomeDataBase, is_secret
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
"""Helpers for config validation using voluptuous."""
|
||||
|
||||
from contextlib import contextmanager
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
from contextlib import contextmanager
|
||||
import uuid as uuid_
|
||||
from datetime import datetime
|
||||
from string import ascii_letters, digits
|
||||
import uuid as uuid_
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
|
@ -17,37 +17,37 @@ from esphome.config_helpers import Extend, Remove
|
|||
from esphome.const import (
|
||||
ALLOWED_NAME_CHARS,
|
||||
CONF_AVAILABILITY,
|
||||
CONF_COMMAND_TOPIC,
|
||||
CONF_COMMAND_RETAIN,
|
||||
CONF_COMMAND_TOPIC,
|
||||
CONF_DAY,
|
||||
CONF_DISABLED_BY_DEFAULT,
|
||||
CONF_DISCOVERY,
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_HOUR,
|
||||
CONF_ICON,
|
||||
CONF_ID,
|
||||
CONF_INTERNAL,
|
||||
CONF_MINUTE,
|
||||
CONF_MONTH,
|
||||
CONF_NAME,
|
||||
CONF_PASSWORD,
|
||||
CONF_PATH,
|
||||
CONF_PAYLOAD_AVAILABLE,
|
||||
CONF_PAYLOAD_NOT_AVAILABLE,
|
||||
CONF_RETAIN,
|
||||
CONF_QOS,
|
||||
CONF_REF,
|
||||
CONF_RETAIN,
|
||||
CONF_SECOND,
|
||||
CONF_SETUP_PRIORITY,
|
||||
CONF_STATE_TOPIC,
|
||||
CONF_TOPIC,
|
||||
CONF_YEAR,
|
||||
CONF_MONTH,
|
||||
CONF_DAY,
|
||||
CONF_HOUR,
|
||||
CONF_MINUTE,
|
||||
CONF_SECOND,
|
||||
CONF_VALUE,
|
||||
CONF_UPDATE_INTERVAL,
|
||||
CONF_TYPE_ID,
|
||||
CONF_TYPE,
|
||||
CONF_REF,
|
||||
CONF_TYPE_ID,
|
||||
CONF_UPDATE_INTERVAL,
|
||||
CONF_URL,
|
||||
CONF_PATH,
|
||||
CONF_USERNAME,
|
||||
CONF_PASSWORD,
|
||||
CONF_VALUE,
|
||||
CONF_YEAR,
|
||||
ENTITY_CATEGORY_CONFIG,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
ENTITY_CATEGORY_NONE,
|
||||
|
@ -72,15 +72,15 @@ from esphome.core import (
|
|||
TimePeriod,
|
||||
TimePeriodMicroseconds,
|
||||
TimePeriodMilliseconds,
|
||||
TimePeriodMinutes,
|
||||
TimePeriodNanoseconds,
|
||||
TimePeriodSeconds,
|
||||
TimePeriodMinutes,
|
||||
)
|
||||
from esphome.helpers import list_starts_with, add_class_to_obj
|
||||
from esphome.helpers import add_class_to_obj, list_starts_with
|
||||
from esphome.schema_extractors import (
|
||||
SCHEMA_EXTRACT,
|
||||
schema_extractor_list,
|
||||
schema_extractor,
|
||||
schema_extractor_list,
|
||||
schema_extractor_registry,
|
||||
schema_extractor_typed,
|
||||
)
|
||||
|
@ -92,7 +92,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||
|
||||
# pylint: disable=consider-using-f-string
|
||||
VARIABLE_PROG = re.compile(
|
||||
"\\$([{0}]+|\\{{[{0}]*\\}})".format(VALID_SUBSTITUTIONS_CHARACTERS)
|
||||
f"\\$([{VALID_SUBSTITUTIONS_CHARACTERS}]+|\\{{[{VALID_SUBSTITUTIONS_CHARACTERS}]*\\}})"
|
||||
)
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
|
@ -465,6 +465,7 @@ zero_to_one_float = float_range(min=0, max=1)
|
|||
negative_one_to_one_float = float_range(min=-1, max=1)
|
||||
positive_int = int_range(min=0)
|
||||
positive_not_null_int = int_range(min=0, min_included=False)
|
||||
positive_not_null_float = float_range(min=0, min_included=False)
|
||||
|
||||
|
||||
def validate_id_name(value):
|
||||
|
@ -1691,9 +1692,9 @@ class SplitDefault(Optional):
|
|||
if CORE.is_esp32:
|
||||
from esphome.components.esp32 import get_esp32_variant
|
||||
from esphome.components.esp32.const import (
|
||||
VARIANT_ESP32C3,
|
||||
VARIANT_ESP32S2,
|
||||
VARIANT_ESP32S3,
|
||||
VARIANT_ESP32C3,
|
||||
)
|
||||
|
||||
variant = get_esp32_variant()
|
||||
|
@ -2188,3 +2189,13 @@ SOURCE_SCHEMA = Any(
|
|||
}
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def rename_key(old_key, new_key):
|
||||
def validator(config: dict) -> dict:
|
||||
config = config.copy()
|
||||
if old_key in config:
|
||||
config[new_key] = config.pop(old_key)
|
||||
return config
|
||||
|
||||
return validator
|
||||
|
|
|
@ -39,8 +39,10 @@ CONF_ACCELERATION_Y = "acceleration_y"
|
|||
CONF_ACCELERATION_Z = "acceleration_z"
|
||||
CONF_ACCURACY = "accuracy"
|
||||
CONF_ACCURACY_DECIMALS = "accuracy_decimals"
|
||||
CONF_ACTION = "action"
|
||||
CONF_ACTION_ID = "action_id"
|
||||
CONF_ACTION_STATE_TOPIC = "action_state_topic"
|
||||
CONF_ACTIONS = "actions"
|
||||
CONF_ACTIVE = "active"
|
||||
CONF_ACTIVE_POWER = "active_power"
|
||||
CONF_ACTUAL_GAIN = "actual_gain"
|
||||
|
@ -503,6 +505,7 @@ CONF_MOTION = "motion"
|
|||
CONF_MOVEMENT_COUNTER = "movement_counter"
|
||||
CONF_MQTT = "mqtt"
|
||||
CONF_MQTT_ID = "mqtt_id"
|
||||
CONF_MULTIPLE = "multiple"
|
||||
CONF_MULTIPLEXER = "multiplexer"
|
||||
CONF_MULTIPLY = "multiply"
|
||||
CONF_NAME = "name"
|
||||
|
@ -541,6 +544,7 @@ CONF_ON_DOUBLE_CLICK = "on_double_click"
|
|||
CONF_ON_ENROLLMENT_DONE = "on_enrollment_done"
|
||||
CONF_ON_ENROLLMENT_FAILED = "on_enrollment_failed"
|
||||
CONF_ON_ENROLLMENT_SCAN = "on_enrollment_scan"
|
||||
CONF_ON_ERROR = "on_error"
|
||||
CONF_ON_EVENT = "on_event"
|
||||
CONF_ON_FINGER_SCAN_INVALID = "on_finger_scan_invalid"
|
||||
CONF_ON_FINGER_SCAN_MATCHED = "on_finger_scan_matched"
|
||||
|
@ -1034,11 +1038,13 @@ UNIT_KILOWATT_HOURS = "kWh"
|
|||
UNIT_LUX = "lx"
|
||||
UNIT_METER = "m"
|
||||
UNIT_METER_PER_SECOND_SQUARED = "m/s²"
|
||||
UNIT_MICROAMP = "µA"
|
||||
UNIT_MICROGRAMS_PER_CUBIC_METER = "µg/m³"
|
||||
UNIT_MICROMETER = "µm"
|
||||
UNIT_MICROSIEMENS_PER_CENTIMETER = "µS/cm"
|
||||
UNIT_MICROSILVERTS_PER_HOUR = "µSv/h"
|
||||
UNIT_MICROTESLA = "µT"
|
||||
UNIT_MILLIAMP = "mA"
|
||||
UNIT_MILLIGRAMS_PER_CUBIC_METER = "mg/m³"
|
||||
UNIT_MILLIMETER = "mm"
|
||||
UNIT_MILLISECOND = "ms"
|
||||
|
|
|
@ -7,11 +7,11 @@ from typing import TYPE_CHECKING, Optional, Union
|
|||
from esphome.const import (
|
||||
CONF_COMMENT,
|
||||
CONF_ESPHOME,
|
||||
CONF_USE_ADDRESS,
|
||||
CONF_ETHERNET,
|
||||
CONF_PORT,
|
||||
CONF_USE_ADDRESS,
|
||||
CONF_WEB_SERVER,
|
||||
CONF_WIFI,
|
||||
CONF_PORT,
|
||||
KEY_CORE,
|
||||
KEY_TARGET_FRAMEWORK,
|
||||
KEY_TARGET_PLATFORM,
|
||||
|
@ -23,11 +23,14 @@ from esphome.const import (
|
|||
PLATFORM_RP2040,
|
||||
PLATFORM_RTL87XX,
|
||||
)
|
||||
from esphome.coroutine import FakeAwaitable as _FakeAwaitable
|
||||
from esphome.coroutine import FakeEventLoop as _FakeEventLoop
|
||||
|
||||
# pylint: disable=unused-import
|
||||
from esphome.coroutine import coroutine, coroutine_with_priority # noqa
|
||||
from esphome.coroutine import ( # noqa: F401
|
||||
FakeAwaitable as _FakeAwaitable,
|
||||
FakeEventLoop as _FakeEventLoop,
|
||||
coroutine,
|
||||
coroutine_with_priority,
|
||||
)
|
||||
from esphome.helpers import ensure_unique_string, get_str_env, is_ha_addon
|
||||
from esphome.util import OrderedDict
|
||||
|
||||
|
@ -334,7 +337,7 @@ class ID:
|
|||
else:
|
||||
self.is_manual = is_manual
|
||||
self.is_declaration = is_declaration
|
||||
self.type: Optional["MockObjClass"] = type
|
||||
self.type: Optional[MockObjClass] = type
|
||||
|
||||
def resolve(self, registered_ids):
|
||||
from esphome.config_validation import RESERVED_IDS
|
||||
|
@ -498,7 +501,7 @@ class EsphomeCore:
|
|||
# The relative path to where all build files are stored
|
||||
self.build_path: Optional[str] = None
|
||||
# The validated configuration, this is None until the config has been validated
|
||||
self.config: Optional["ConfigType"] = None
|
||||
self.config: Optional[ConfigType] = None
|
||||
# The pending tasks in the task queue (mostly for C++ generation)
|
||||
# This is a priority queue (with heapq)
|
||||
# Each item is a tuple of form: (-priority, unique number, task)
|
||||
|
@ -506,17 +509,17 @@ class EsphomeCore:
|
|||
# Task counter for pending tasks
|
||||
self.task_counter = 0
|
||||
# The variable cache, for each ID this holds a MockObj of the variable obj
|
||||
self.variables: dict[str, "MockObj"] = {}
|
||||
self.variables: dict[str, MockObj] = {}
|
||||
# A list of statements that go in the main setup() block
|
||||
self.main_statements: list["Statement"] = []
|
||||
self.main_statements: list[Statement] = []
|
||||
# A list of statements to insert in the global block (includes and global variables)
|
||||
self.global_statements: list["Statement"] = []
|
||||
self.global_statements: list[Statement] = []
|
||||
# A set of platformio libraries to add to the project
|
||||
self.libraries: list[Library] = []
|
||||
# A set of build flags to set in the platformio project
|
||||
self.build_flags: set[str] = set()
|
||||
# A set of defines to set for the compile process in esphome/core/defines.h
|
||||
self.defines: set["Define"] = set()
|
||||
self.defines: set[Define] = set()
|
||||
# A map of all platformio options to apply
|
||||
self.platformio_options: dict[str, Union[str, list[str]]] = {}
|
||||
# A set of strings of names of loaded integrations, used to find namespace ID conflicts
|
||||
|
|
|
@ -3,9 +3,10 @@ import multiprocessing
|
|||
import os
|
||||
import re
|
||||
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.zephyr import zephyr_add_prj_conf
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ARDUINO_VERSION,
|
||||
CONF_AREA,
|
||||
|
@ -16,11 +17,11 @@ from esphome.const import (
|
|||
CONF_COMPILE_PROCESS_LIMIT,
|
||||
CONF_ESPHOME,
|
||||
CONF_FRAMEWORK,
|
||||
CONF_FRIENDLY_NAME,
|
||||
CONF_INCLUDES,
|
||||
CONF_LIBRARIES,
|
||||
CONF_MIN_VERSION,
|
||||
CONF_NAME,
|
||||
CONF_FRIENDLY_NAME,
|
||||
CONF_ON_BOOT,
|
||||
CONF_ON_LOOP,
|
||||
CONF_ON_SHUTDOWN,
|
||||
|
@ -34,13 +35,12 @@ from esphome.const import (
|
|||
CONF_TYPE,
|
||||
CONF_VERSION,
|
||||
KEY_CORE,
|
||||
TARGET_PLATFORMS,
|
||||
PLATFORM_ESP8266,
|
||||
TARGET_PLATFORMS,
|
||||
__version__ as ESPHOME_VERSION,
|
||||
)
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
from esphome.helpers import copy_file_if_changed, get_str_env, walk_files
|
||||
from esphome.components.zephyr import zephyr_add_prj_conf
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
@ -39,8 +39,12 @@
|
|||
#define USE_LOCK
|
||||
#define USE_LOGGER
|
||||
#define USE_LVGL
|
||||
#define USE_LVGL_BINARY_SENSOR
|
||||
#define USE_LVGL_FONT
|
||||
#define USE_LVGL_IMAGE
|
||||
#define USE_LVGL_KEY_LISTENER
|
||||
#define USE_LVGL_TOUCHSCREEN
|
||||
#define USE_LVGL_ROTARY_ENCODER
|
||||
#define USE_MDNS
|
||||
#define USE_MEDIA_PLAYER
|
||||
#define USE_MQTT
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import esphome.final_validate as fv
|
||||
|
||||
from esphome.const import CONF_ID
|
||||
import esphome.final_validate as fv
|
||||
|
||||
|
||||
def inherit_property_from(property_to_inherit, parent_id_property, transform=None):
|
||||
|
|
|
@ -43,13 +43,13 @@ the last `yield` expression defines what is returned.
|
|||
"""
|
||||
|
||||
import collections
|
||||
from collections.abc import Awaitable, Generator, Iterator
|
||||
import functools
|
||||
import heapq
|
||||
import inspect
|
||||
import logging
|
||||
import types
|
||||
from typing import Any, Callable
|
||||
from collections.abc import Awaitable, Generator, Iterator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import abc
|
||||
from collections.abc import Sequence
|
||||
import inspect
|
||||
import math
|
||||
import re
|
||||
from collections.abc import Sequence
|
||||
from typing import Any, Callable, Optional, Union
|
||||
|
||||
from esphome.core import (
|
||||
|
|
|
@ -12,15 +12,13 @@ from esphome.const import (
|
|||
CONF_UPDATE_INTERVAL,
|
||||
KEY_PAST_SAFE_MODE,
|
||||
)
|
||||
|
||||
from esphome.core import coroutine, ID, CORE
|
||||
from esphome.core import CORE, ID, coroutine
|
||||
from esphome.coroutine import FakeAwaitable
|
||||
from esphome.types import ConfigType, ConfigFragmentType
|
||||
from esphome.cpp_generator import add, get_variable
|
||||
from esphome.cpp_types import App
|
||||
from esphome.helpers import sanitize, snake_case
|
||||
from esphome.types import ConfigFragmentType, ConfigType
|
||||
from esphome.util import Registry, RegistryEntry
|
||||
from esphome.helpers import snake_case, sanitize
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Coroutine
|
||||
import contextlib
|
||||
import logging
|
||||
import threading
|
||||
from dataclasses import dataclass
|
||||
from functools import partial
|
||||
import logging
|
||||
import threading
|
||||
from typing import TYPE_CHECKING, Any, Callable
|
||||
from collections.abc import Coroutine
|
||||
|
||||
from ..zeroconf import DiscoveredImport
|
||||
from .dns import DNSCache
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from asyncio import events
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
import logging
|
||||
import os
|
||||
import socket
|
||||
import threading
|
||||
import traceback
|
||||
from asyncio import events
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from time import monotonic
|
||||
import traceback
|
||||
from typing import Any
|
||||
|
||||
from esphome.storage_json import EsphomeStorageJSON, esphome_storage_path
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections import defaultdict
|
||||
import logging
|
||||
import os
|
||||
from collections import defaultdict
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from esphome import const, util
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import logging
|
||||
import os
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ from __future__ import annotations
|
|||
|
||||
import asyncio
|
||||
import base64
|
||||
from collections.abc import Iterable
|
||||
import datetime
|
||||
import functools
|
||||
import gzip
|
||||
|
@ -9,13 +10,12 @@ import hashlib
|
|||
import json
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
import secrets
|
||||
import shutil
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
from collections.abc import Iterable
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, Callable, TypeVar
|
||||
from urllib.parse import urlparse
|
||||
|
||||
|
@ -26,13 +26,13 @@ import tornado.httpserver
|
|||
import tornado.httputil
|
||||
import tornado.ioloop
|
||||
import tornado.iostream
|
||||
from tornado.log import access_log
|
||||
import tornado.netutil
|
||||
import tornado.process
|
||||
import tornado.queues
|
||||
import tornado.web
|
||||
import tornado.websocket
|
||||
import yaml
|
||||
from tornado.log import access_log
|
||||
from yaml.nodes import Node
|
||||
|
||||
from esphome import const, platformio_api, yaml_util
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from pathlib import Path
|
||||
import os
|
||||
from datetime import datetime
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import requests
|
||||
|
||||
import esphome.config_validation as cv
|
||||
from esphome.core import CORE, TimePeriodSeconds
|
||||
from esphome.const import __version__
|
||||
from esphome.core import CORE, TimePeriodSeconds
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
CODEOWNERS = ["@landonr"]
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from typing import Any
|
||||
import contextvars
|
||||
from typing import Any
|
||||
|
||||
from esphome.types import ConfigFragmentType, ID, ConfigPathType
|
||||
import esphome.config_validation as cv
|
||||
from esphome.types import ID, ConfigFragmentType, ConfigPathType
|
||||
|
||||
|
||||
class FinalValidateConfig(ABC):
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import hashlib
|
||||
import logging
|
||||
import re
|
||||
import subprocess
|
||||
import urllib.parse
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
import hashlib
|
||||
import logging
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
from typing import Callable, Optional
|
||||
import urllib.parse
|
||||
|
||||
import esphome.config_validation as cv
|
||||
from esphome.core import CORE, TimePeriodSeconds
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import codecs
|
||||
from contextlib import suppress
|
||||
|
||||
import logging
|
||||
import os
|
||||
import platform
|
||||
from pathlib import Path
|
||||
from typing import Union
|
||||
import tempfile
|
||||
from urllib.parse import urlparse
|
||||
import platform
|
||||
import re
|
||||
import tempfile
|
||||
from typing import Union
|
||||
from urllib.parse import urlparse
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -129,9 +128,10 @@ def _resolve_with_zeroconf(host):
|
|||
|
||||
|
||||
def resolve_ip_address(host):
|
||||
from esphome.core import EsphomeError
|
||||
import socket
|
||||
|
||||
from esphome.core import EsphomeError
|
||||
|
||||
errs = []
|
||||
|
||||
if host.endswith(".local"):
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
from datetime import datetime
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
import ssl
|
||||
import sys
|
||||
import time
|
||||
import json
|
||||
|
||||
import paho.mqtt.client as mqtt
|
||||
|
||||
|
@ -24,9 +24,9 @@ from esphome.const import (
|
|||
CONF_USERNAME,
|
||||
)
|
||||
from esphome.core import CORE, EsphomeError
|
||||
from esphome.log import color, Fore
|
||||
from esphome.helpers import get_int_env, get_str_env
|
||||
from esphome.log import Fore, color
|
||||
from esphome.util import safe_print
|
||||
from esphome.helpers import get_str_env, get_int_env
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue