mirror of
https://github.com/esphome/esphome.git
synced 2025-03-01 08:18:16 +00:00
Merge branch 'dev' into component_climate_ir_samsung
This commit is contained in:
commit
97352c9151
@ -758,6 +758,14 @@ def parse_args(argv):
|
|||||||
options_parser.add_argument(
|
options_parser.add_argument(
|
||||||
"-q", "--quiet", help="Disable all ESPHome logs.", action="store_true"
|
"-q", "--quiet", help="Disable all ESPHome logs.", action="store_true"
|
||||||
)
|
)
|
||||||
|
options_parser.add_argument(
|
||||||
|
"-l",
|
||||||
|
"--log-level",
|
||||||
|
help="Set the log level.",
|
||||||
|
default=os.getenv("ESPHOME_LOG_LEVEL", "INFO"),
|
||||||
|
action="store",
|
||||||
|
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
|
||||||
|
)
|
||||||
options_parser.add_argument(
|
options_parser.add_argument(
|
||||||
"--dashboard", help=argparse.SUPPRESS, action="store_true"
|
"--dashboard", help=argparse.SUPPRESS, action="store_true"
|
||||||
)
|
)
|
||||||
@ -987,11 +995,16 @@ def run_esphome(argv):
|
|||||||
args = parse_args(argv)
|
args = parse_args(argv)
|
||||||
CORE.dashboard = args.dashboard
|
CORE.dashboard = args.dashboard
|
||||||
|
|
||||||
|
# Override log level if verbose is set
|
||||||
|
if args.verbose:
|
||||||
|
args.log_level = "DEBUG"
|
||||||
|
elif args.quiet:
|
||||||
|
args.log_level = "CRITICAL"
|
||||||
|
|
||||||
setup_log(
|
setup_log(
|
||||||
args.verbose,
|
log_level=args.log_level,
|
||||||
args.quiet,
|
|
||||||
# Show timestamp for dashboard access logs
|
# Show timestamp for dashboard access logs
|
||||||
args.command == "dashboard",
|
include_timestamp=args.command == "dashboard",
|
||||||
)
|
)
|
||||||
|
|
||||||
if args.command in PRE_CONFIG_ACTIONS:
|
if args.command in PRE_CONFIG_ACTIONS:
|
||||||
|
@ -37,8 +37,9 @@ void ClimateIR::setup() {
|
|||||||
this->publish_state();
|
this->publish_state();
|
||||||
});
|
});
|
||||||
this->current_temperature = this->sensor_->state;
|
this->current_temperature = this->sensor_->state;
|
||||||
} else
|
} else {
|
||||||
this->current_temperature = NAN;
|
this->current_temperature = NAN;
|
||||||
|
}
|
||||||
// restore set points
|
// restore set points
|
||||||
auto restore = this->restore_state_();
|
auto restore = this->restore_state_();
|
||||||
if (restore.has_value()) {
|
if (restore.has_value()) {
|
||||||
|
@ -131,8 +131,9 @@ bool CoolixClimate::on_coolix(climate::Climate *parent, remote_base::RemoteRecei
|
|||||||
} else {
|
} else {
|
||||||
parent->mode = climate::CLIMATE_MODE_FAN_ONLY;
|
parent->mode = climate::CLIMATE_MODE_FAN_ONLY;
|
||||||
}
|
}
|
||||||
} else
|
} else {
|
||||||
parent->mode = climate::CLIMATE_MODE_COOL;
|
parent->mode = climate::CLIMATE_MODE_COOL;
|
||||||
|
}
|
||||||
|
|
||||||
// Fan Speed
|
// Fan Speed
|
||||||
if ((remote_state & COOLIX_FAN_AUTO) == COOLIX_FAN_AUTO || parent->mode == climate::CLIMATE_MODE_HEAT_COOL ||
|
if ((remote_state & COOLIX_FAN_AUTO) == COOLIX_FAN_AUTO || parent->mode == climate::CLIMATE_MODE_HEAT_COOL ||
|
||||||
|
@ -118,8 +118,9 @@ std::unique_ptr<Command> CircularCommandQueue::dequeue() {
|
|||||||
if (front_ == rear_) {
|
if (front_ == rear_) {
|
||||||
front_ = -1;
|
front_ = -1;
|
||||||
rear_ = -1;
|
rear_ = -1;
|
||||||
} else
|
} else {
|
||||||
front_ = (front_ + 1) % COMMAND_QUEUE_SIZE;
|
front_ = (front_ + 1) % COMMAND_QUEUE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
return dequeued_cmd;
|
return dequeued_cmd;
|
||||||
}
|
}
|
||||||
|
@ -157,10 +157,11 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r
|
|||||||
if (bit == 0) {
|
if (bit == 0) {
|
||||||
bit = 7;
|
bit = 7;
|
||||||
byte++;
|
byte++;
|
||||||
} else
|
} else {
|
||||||
bit--;
|
bit--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (!report_errors && error_code != 0)
|
if (!report_errors && error_code != 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ DisplayOnPageChangeTrigger = display_ns.class_(
|
|||||||
|
|
||||||
CONF_ON_PAGE_CHANGE = "on_page_change"
|
CONF_ON_PAGE_CHANGE = "on_page_change"
|
||||||
CONF_SHOW_TEST_CARD = "show_test_card"
|
CONF_SHOW_TEST_CARD = "show_test_card"
|
||||||
|
CONF_UNSPECIFIED = "unspecified"
|
||||||
|
|
||||||
DISPLAY_ROTATIONS = {
|
DISPLAY_ROTATIONS = {
|
||||||
0: display_ns.DISPLAY_ROTATION_0_DEGREES,
|
0: display_ns.DISPLAY_ROTATION_0_DEGREES,
|
||||||
@ -55,16 +56,22 @@ def validate_rotation(value):
|
|||||||
return cv.enum(DISPLAY_ROTATIONS, int=True)(value)
|
return cv.enum(DISPLAY_ROTATIONS, int=True)(value)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_auto_clear(value):
|
||||||
|
if value == CONF_UNSPECIFIED:
|
||||||
|
return value
|
||||||
|
return cv.boolean(value)
|
||||||
|
|
||||||
|
|
||||||
BASIC_DISPLAY_SCHEMA = cv.Schema(
|
BASIC_DISPLAY_SCHEMA = cv.Schema(
|
||||||
{
|
{
|
||||||
cv.Optional(CONF_LAMBDA): cv.lambda_,
|
cv.Exclusive(CONF_LAMBDA, CONF_LAMBDA): cv.lambda_,
|
||||||
}
|
}
|
||||||
).extend(cv.polling_component_schema("1s"))
|
).extend(cv.polling_component_schema("1s"))
|
||||||
|
|
||||||
FULL_DISPLAY_SCHEMA = BASIC_DISPLAY_SCHEMA.extend(
|
FULL_DISPLAY_SCHEMA = BASIC_DISPLAY_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
cv.Optional(CONF_ROTATION): validate_rotation,
|
cv.Optional(CONF_ROTATION): validate_rotation,
|
||||||
cv.Optional(CONF_PAGES): cv.All(
|
cv.Exclusive(CONF_PAGES, CONF_LAMBDA): cv.All(
|
||||||
cv.ensure_list(
|
cv.ensure_list(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(DisplayPage),
|
cv.GenerateID(): cv.declare_id(DisplayPage),
|
||||||
@ -82,7 +89,9 @@ FULL_DISPLAY_SCHEMA = BASIC_DISPLAY_SCHEMA.extend(
|
|||||||
cv.Optional(CONF_TO): cv.use_id(DisplayPage),
|
cv.Optional(CONF_TO): cv.use_id(DisplayPage),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_AUTO_CLEAR_ENABLED, default=True): cv.boolean,
|
cv.Optional(
|
||||||
|
CONF_AUTO_CLEAR_ENABLED, default=CONF_UNSPECIFIED
|
||||||
|
): validate_auto_clear,
|
||||||
cv.Optional(CONF_SHOW_TEST_CARD): cv.boolean,
|
cv.Optional(CONF_SHOW_TEST_CARD): cv.boolean,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -92,8 +101,12 @@ async def setup_display_core_(var, config):
|
|||||||
if CONF_ROTATION in config:
|
if CONF_ROTATION in config:
|
||||||
cg.add(var.set_rotation(DISPLAY_ROTATIONS[config[CONF_ROTATION]]))
|
cg.add(var.set_rotation(DISPLAY_ROTATIONS[config[CONF_ROTATION]]))
|
||||||
|
|
||||||
if CONF_AUTO_CLEAR_ENABLED in config:
|
if auto_clear := config.get(CONF_AUTO_CLEAR_ENABLED):
|
||||||
cg.add(var.set_auto_clear(config[CONF_AUTO_CLEAR_ENABLED]))
|
# Default to true if pages or lambda is specified. Ideally this would be done during validation, but
|
||||||
|
# the possible schemas are too complex to do this easily.
|
||||||
|
if auto_clear == CONF_UNSPECIFIED:
|
||||||
|
auto_clear = CONF_LAMBDA in config or CONF_PAGES in config
|
||||||
|
cg.add(var.set_auto_clear(auto_clear))
|
||||||
|
|
||||||
if CONF_PAGES in config:
|
if CONF_PAGES in config:
|
||||||
pages = []
|
pages = []
|
||||||
|
@ -266,9 +266,10 @@ void Display::filled_gauge(int center_x, int center_y, int radius1, int radius2,
|
|||||||
if (dymax < float(-dxmax) * tan_a) {
|
if (dymax < float(-dxmax) * tan_a) {
|
||||||
upd_dxmax = ceil(float(dymax) / tan_a);
|
upd_dxmax = ceil(float(dymax) / tan_a);
|
||||||
hline_width = -dxmax - upd_dxmax + 1;
|
hline_width = -dxmax - upd_dxmax + 1;
|
||||||
} else
|
} else {
|
||||||
hline_width = 0;
|
hline_width = 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (hline_width > 0)
|
if (hline_width > 0)
|
||||||
this->horizontal_line(center_x + dxmax, center_y - dymax, hline_width, color);
|
this->horizontal_line(center_x + dxmax, center_y - dymax, hline_width, color);
|
||||||
}
|
}
|
||||||
|
@ -90,8 +90,9 @@ void Rect::info(const std::string &prefix) {
|
|||||||
if (this->is_set()) {
|
if (this->is_set()) {
|
||||||
ESP_LOGI(TAG, "%s [%3d,%3d,%3d,%3d] (%3d,%3d)", prefix.c_str(), this->x, this->y, this->w, this->h, this->x2(),
|
ESP_LOGI(TAG, "%s [%3d,%3d,%3d,%3d] (%3d,%3d)", prefix.c_str(), this->x, this->y, this->w, this->h, this->x2(),
|
||||||
this->y2());
|
this->y2());
|
||||||
} else
|
} else {
|
||||||
ESP_LOGI(TAG, "%s ** IS NOT SET **", prefix.c_str());
|
ESP_LOGI(TAG, "%s ** IS NOT SET **", prefix.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace display
|
} // namespace display
|
||||||
|
@ -112,7 +112,7 @@ void ESP32ImprovComponent::loop() {
|
|||||||
this->set_state_(improv::STATE_AUTHORIZED);
|
this->set_state_(improv::STATE_AUTHORIZED);
|
||||||
} else
|
} else
|
||||||
#else
|
#else
|
||||||
this->set_state_(improv::STATE_AUTHORIZED);
|
{ this->set_state_(improv::STATE_AUTHORIZED); }
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
if (!this->check_identify_())
|
if (!this->check_identify_())
|
||||||
|
@ -94,11 +94,11 @@ CLK_MODES = {
|
|||||||
|
|
||||||
MANUAL_IP_SCHEMA = cv.Schema(
|
MANUAL_IP_SCHEMA = cv.Schema(
|
||||||
{
|
{
|
||||||
cv.Required(CONF_STATIC_IP): cv.ipv4,
|
cv.Required(CONF_STATIC_IP): cv.ipv4address,
|
||||||
cv.Required(CONF_GATEWAY): cv.ipv4,
|
cv.Required(CONF_GATEWAY): cv.ipv4address,
|
||||||
cv.Required(CONF_SUBNET): cv.ipv4,
|
cv.Required(CONF_SUBNET): cv.ipv4address,
|
||||||
cv.Optional(CONF_DNS1, default="0.0.0.0"): cv.ipv4,
|
cv.Optional(CONF_DNS1, default="0.0.0.0"): cv.ipv4address,
|
||||||
cv.Optional(CONF_DNS2, default="0.0.0.0"): cv.ipv4,
|
cv.Optional(CONF_DNS2, default="0.0.0.0"): cv.ipv4address,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -255,11 +255,11 @@ FINAL_VALIDATE_SCHEMA = _final_validate
|
|||||||
def manual_ip(config):
|
def manual_ip(config):
|
||||||
return cg.StructInitializer(
|
return cg.StructInitializer(
|
||||||
ManualIP,
|
ManualIP,
|
||||||
("static_ip", IPAddress(*config[CONF_STATIC_IP].args)),
|
("static_ip", IPAddress(str(config[CONF_STATIC_IP]))),
|
||||||
("gateway", IPAddress(*config[CONF_GATEWAY].args)),
|
("gateway", IPAddress(str(config[CONF_GATEWAY]))),
|
||||||
("subnet", IPAddress(*config[CONF_SUBNET].args)),
|
("subnet", IPAddress(str(config[CONF_SUBNET]))),
|
||||||
("dns1", IPAddress(*config[CONF_DNS1].args)),
|
("dns1", IPAddress(str(config[CONF_DNS1]))),
|
||||||
("dns2", IPAddress(*config[CONF_DNS2].args)),
|
("dns2", IPAddress(str(config[CONF_DNS2]))),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -97,8 +97,9 @@ void GCJA5Component::parse_data_() {
|
|||||||
if (this->rx_message_[0] != 0x02 || this->rx_message_[31] != 0x03 || !this->calculate_checksum_()) {
|
if (this->rx_message_[0] != 0x02 || this->rx_message_[31] != 0x03 || !this->calculate_checksum_()) {
|
||||||
ESP_LOGVV(TAG, "Discarding bad packet - failed checks.");
|
ESP_LOGVV(TAG, "Discarding bad packet - failed checks.");
|
||||||
return;
|
return;
|
||||||
} else
|
} else {
|
||||||
ESP_LOGVV(TAG, "Good packet found.");
|
ESP_LOGVV(TAG, "Good packet found.");
|
||||||
|
}
|
||||||
|
|
||||||
this->have_good_data_ = true;
|
this->have_good_data_ = true;
|
||||||
uint8_t status = this->rx_message_[29];
|
uint8_t status = this->rx_message_[29];
|
||||||
|
@ -342,8 +342,9 @@ bool HaierClimateBase::prepare_pending_action() {
|
|||||||
this->action_request_.reset();
|
this->action_request_.reset();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ClimateTraits HaierClimateBase::traits() { return traits_; }
|
ClimateTraits HaierClimateBase::traits() { return traits_; }
|
||||||
|
@ -710,9 +710,10 @@ void HonClimate::process_alarm_message_(const uint8_t *packet, uint8_t size, boo
|
|||||||
alarm_code++;
|
alarm_code++;
|
||||||
}
|
}
|
||||||
active_alarms_[i] = packet[2 + i];
|
active_alarms_[i] = packet[2 + i];
|
||||||
} else
|
} else {
|
||||||
alarm_code += 8;
|
alarm_code += 8;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
float alarm_count = 0.0f;
|
float alarm_count = 0.0f;
|
||||||
static uint8_t nibble_bits_count[] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
|
static uint8_t nibble_bits_count[] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
|
||||||
|
@ -87,8 +87,9 @@ void HeatpumpIRClimate::setup() {
|
|||||||
this->publish_state();
|
this->publish_state();
|
||||||
});
|
});
|
||||||
this->current_temperature = this->sensor_->state;
|
this->current_temperature = this->sensor_->state;
|
||||||
} else
|
} else {
|
||||||
this->current_temperature = NAN;
|
this->current_temperature = NAN;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeatpumpIRClimate::transmit_state() {
|
void HeatpumpIRClimate::transmit_state() {
|
||||||
|
@ -25,6 +25,7 @@ void I2SAudioMicrophone::setup() {
|
|||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
|
{
|
||||||
if (this->pdm_) {
|
if (this->pdm_) {
|
||||||
if (this->parent_->get_port() != I2S_NUM_0) {
|
if (this->parent_->get_port() != I2S_NUM_0) {
|
||||||
ESP_LOGE(TAG, "PDM only works on I2S0!");
|
ESP_LOGE(TAG, "PDM only works on I2S0!");
|
||||||
@ -32,6 +33,7 @@ void I2SAudioMicrophone::setup() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2SAudioMicrophone::start() {
|
void I2SAudioMicrophone::start() {
|
||||||
|
@ -197,11 +197,11 @@ def final_validation(configs):
|
|||||||
for display_id in config[df.CONF_DISPLAYS]:
|
for display_id in config[df.CONF_DISPLAYS]:
|
||||||
path = global_config.get_path_for_id(display_id)[:-1]
|
path = global_config.get_path_for_id(display_id)[:-1]
|
||||||
display = global_config.get_config_for_path(path)
|
display = global_config.get_config_for_path(path)
|
||||||
if CONF_LAMBDA in display:
|
if CONF_LAMBDA in display or CONF_PAGES in display:
|
||||||
raise cv.Invalid(
|
raise cv.Invalid(
|
||||||
"Using lambda: in display config not compatible with LVGL"
|
"Using lambda: or pages: in display config is not compatible with LVGL"
|
||||||
)
|
)
|
||||||
if display[CONF_AUTO_CLEAR_ENABLED]:
|
if display.get(CONF_AUTO_CLEAR_ENABLED) is True:
|
||||||
raise cv.Invalid(
|
raise cv.Invalid(
|
||||||
"Using auto_clear_enabled: true in display config not compatible with LVGL"
|
"Using auto_clear_enabled: true in display config not compatible with LVGL"
|
||||||
)
|
)
|
||||||
|
@ -4,24 +4,26 @@ from esphome import automation
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ACTION, CONF_GROUP, CONF_ID, CONF_TIMEOUT
|
from esphome.const import CONF_ACTION, CONF_GROUP, CONF_ID, CONF_TIMEOUT
|
||||||
from esphome.cpp_generator import get_variable
|
from esphome.cpp_generator import TemplateArguments, get_variable
|
||||||
from esphome.cpp_types import nullptr
|
from esphome.cpp_types import nullptr
|
||||||
|
|
||||||
from .defines import (
|
from .defines import (
|
||||||
CONF_DISP_BG_COLOR,
|
CONF_DISP_BG_COLOR,
|
||||||
CONF_DISP_BG_IMAGE,
|
CONF_DISP_BG_IMAGE,
|
||||||
|
CONF_DISP_BG_OPA,
|
||||||
CONF_EDITING,
|
CONF_EDITING,
|
||||||
CONF_FREEZE,
|
CONF_FREEZE,
|
||||||
CONF_LVGL_ID,
|
CONF_LVGL_ID,
|
||||||
CONF_SHOW_SNOW,
|
CONF_SHOW_SNOW,
|
||||||
literal,
|
literal,
|
||||||
)
|
)
|
||||||
from .lv_validation import lv_bool, lv_color, lv_image
|
from .lv_validation import lv_bool, lv_color, lv_image, opacity
|
||||||
from .lvcode import (
|
from .lvcode import (
|
||||||
LVGL_COMP_ARG,
|
LVGL_COMP_ARG,
|
||||||
UPDATE_EVENT,
|
UPDATE_EVENT,
|
||||||
LambdaContext,
|
LambdaContext,
|
||||||
LocalVariable,
|
LocalVariable,
|
||||||
|
LvglComponent,
|
||||||
ReturnStatement,
|
ReturnStatement,
|
||||||
add_line_marks,
|
add_line_marks,
|
||||||
lv,
|
lv,
|
||||||
@ -92,7 +94,11 @@ async def lvgl_is_paused(config, condition_id, template_arg, args):
|
|||||||
lvgl = config[CONF_LVGL_ID]
|
lvgl = config[CONF_LVGL_ID]
|
||||||
async with LambdaContext(LVGL_COMP_ARG, return_type=cg.bool_) as context:
|
async with LambdaContext(LVGL_COMP_ARG, return_type=cg.bool_) as context:
|
||||||
lv_add(ReturnStatement(lvgl_comp.is_paused()))
|
lv_add(ReturnStatement(lvgl_comp.is_paused()))
|
||||||
var = cg.new_Pvariable(condition_id, template_arg, await context.get_lambda())
|
var = cg.new_Pvariable(
|
||||||
|
condition_id,
|
||||||
|
TemplateArguments(LvglComponent, *template_arg),
|
||||||
|
await context.get_lambda(),
|
||||||
|
)
|
||||||
await cg.register_parented(var, lvgl)
|
await cg.register_parented(var, lvgl)
|
||||||
return var
|
return var
|
||||||
|
|
||||||
@ -113,19 +119,32 @@ async def lvgl_is_idle(config, condition_id, template_arg, args):
|
|||||||
timeout = await cg.templatable(config[CONF_TIMEOUT], [], cg.uint32)
|
timeout = await cg.templatable(config[CONF_TIMEOUT], [], cg.uint32)
|
||||||
async with LambdaContext(LVGL_COMP_ARG, return_type=cg.bool_) as context:
|
async with LambdaContext(LVGL_COMP_ARG, return_type=cg.bool_) as context:
|
||||||
lv_add(ReturnStatement(lvgl_comp.is_idle(timeout)))
|
lv_add(ReturnStatement(lvgl_comp.is_idle(timeout)))
|
||||||
var = cg.new_Pvariable(condition_id, template_arg, await context.get_lambda())
|
var = cg.new_Pvariable(
|
||||||
|
condition_id,
|
||||||
|
TemplateArguments(LvglComponent, *template_arg),
|
||||||
|
await context.get_lambda(),
|
||||||
|
)
|
||||||
await cg.register_parented(var, lvgl)
|
await cg.register_parented(var, lvgl)
|
||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
async def disp_update(disp, config: dict):
|
async def disp_update(disp, config: dict):
|
||||||
if CONF_DISP_BG_COLOR not in config and CONF_DISP_BG_IMAGE not in config:
|
if (
|
||||||
|
CONF_DISP_BG_COLOR not in config
|
||||||
|
and CONF_DISP_BG_IMAGE not in config
|
||||||
|
and CONF_DISP_BG_OPA not in config
|
||||||
|
):
|
||||||
return
|
return
|
||||||
with LocalVariable("lv_disp_tmp", lv_disp_t, disp) as disp_temp:
|
with LocalVariable("lv_disp_tmp", lv_disp_t, disp) as disp_temp:
|
||||||
if (bg_color := config.get(CONF_DISP_BG_COLOR)) is not None:
|
if (bg_color := config.get(CONF_DISP_BG_COLOR)) is not None:
|
||||||
lv.disp_set_bg_color(disp_temp, await lv_color.process(bg_color))
|
lv.disp_set_bg_color(disp_temp, await lv_color.process(bg_color))
|
||||||
if bg_image := config.get(CONF_DISP_BG_IMAGE):
|
if bg_image := config.get(CONF_DISP_BG_IMAGE):
|
||||||
|
if bg_image == "none":
|
||||||
|
lv.disp_set_bg_image(disp_temp, static_cast("void *", "nullptr"))
|
||||||
|
else:
|
||||||
lv.disp_set_bg_image(disp_temp, await lv_image.process(bg_image))
|
lv.disp_set_bg_image(disp_temp, await lv_image.process(bg_image))
|
||||||
|
if (bg_opa := config.get(CONF_DISP_BG_OPA)) is not None:
|
||||||
|
lv.disp_set_bg_opa(disp_temp, await opacity.process(bg_opa))
|
||||||
|
|
||||||
|
|
||||||
@automation.register_action(
|
@automation.register_action(
|
||||||
|
@ -215,7 +215,7 @@ LV_LONG_MODES = LvConstant(
|
|||||||
)
|
)
|
||||||
|
|
||||||
STATES = (
|
STATES = (
|
||||||
"default",
|
# default state not included here
|
||||||
"checked",
|
"checked",
|
||||||
"focused",
|
"focused",
|
||||||
"focus_key",
|
"focus_key",
|
||||||
@ -403,6 +403,7 @@ CONF_COLUMN = "column"
|
|||||||
CONF_DIGITS = "digits"
|
CONF_DIGITS = "digits"
|
||||||
CONF_DISP_BG_COLOR = "disp_bg_color"
|
CONF_DISP_BG_COLOR = "disp_bg_color"
|
||||||
CONF_DISP_BG_IMAGE = "disp_bg_image"
|
CONF_DISP_BG_IMAGE = "disp_bg_image"
|
||||||
|
CONF_DISP_BG_OPA = "disp_bg_opa"
|
||||||
CONF_BODY = "body"
|
CONF_BODY = "body"
|
||||||
CONF_BUTTONS = "buttons"
|
CONF_BUTTONS = "buttons"
|
||||||
CONF_BYTE_ORDER = "byte_order"
|
CONF_BYTE_ORDER = "byte_order"
|
||||||
|
@ -119,6 +119,7 @@ void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_ev
|
|||||||
}
|
}
|
||||||
void LvglComponent::add_page(LvPageType *page) {
|
void LvglComponent::add_page(LvPageType *page) {
|
||||||
this->pages_.push_back(page);
|
this->pages_.push_back(page);
|
||||||
|
page->set_parent(this);
|
||||||
page->setup(this->pages_.size() - 1);
|
page->setup(this->pages_.size() - 1);
|
||||||
}
|
}
|
||||||
void LvglComponent::show_page(size_t index, lv_scr_load_anim_t anim, uint32_t time) {
|
void LvglComponent::show_page(size_t index, lv_scr_load_anim_t anim, uint32_t time) {
|
||||||
@ -143,6 +144,8 @@ void LvglComponent::show_prev_page(lv_scr_load_anim_t anim, uint32_t time) {
|
|||||||
} while (this->pages_[this->current_page_]->skip); // skip empty pages()
|
} while (this->pages_[this->current_page_]->skip); // skip empty pages()
|
||||||
this->show_page(this->current_page_, anim, time);
|
this->show_page(this->current_page_, anim, time);
|
||||||
}
|
}
|
||||||
|
size_t LvglComponent::get_current_page() const { return this->current_page_; }
|
||||||
|
bool LvPageType::is_showing() const { return this->parent_->get_current_page() == this->index; }
|
||||||
void LvglComponent::draw_buffer_(const lv_area_t *area, lv_color_t *ptr) {
|
void LvglComponent::draw_buffer_(const lv_area_t *area, lv_color_t *ptr) {
|
||||||
auto width = lv_area_get_width(area);
|
auto width = lv_area_get_width(area);
|
||||||
auto height = lv_area_get_height(area);
|
auto height = lv_area_get_height(area);
|
||||||
|
@ -59,6 +59,16 @@ inline void lv_img_set_src(lv_obj_t *obj, esphome::image::Image *image) {
|
|||||||
inline void lv_disp_set_bg_image(lv_disp_t *disp, esphome::image::Image *image) {
|
inline void lv_disp_set_bg_image(lv_disp_t *disp, esphome::image::Image *image) {
|
||||||
lv_disp_set_bg_image(disp, image->get_lv_img_dsc());
|
lv_disp_set_bg_image(disp, image->get_lv_img_dsc());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void lv_obj_set_style_bg_img_src(lv_obj_t *obj, esphome::image::Image *image, lv_style_selector_t selector) {
|
||||||
|
lv_obj_set_style_bg_img_src(obj, image->get_lv_img_dsc(), selector);
|
||||||
|
}
|
||||||
|
#ifdef USE_LVGL_METER
|
||||||
|
inline lv_meter_indicator_t *lv_meter_add_needle_img(lv_obj_t *obj, lv_meter_scale_t *scale, esphome::image::Image *src,
|
||||||
|
lv_coord_t pivot_x, lv_coord_t pivot_y) {
|
||||||
|
return lv_meter_add_needle_img(obj, scale, src->get_lv_img_dsc(), pivot_x, pivot_y);
|
||||||
|
}
|
||||||
|
#endif // USE_LVGL_METER
|
||||||
#endif // USE_LVGL_IMAGE
|
#endif // USE_LVGL_IMAGE
|
||||||
#ifdef USE_LVGL_ANIMIMG
|
#ifdef USE_LVGL_ANIMIMG
|
||||||
inline void lv_animimg_set_src(lv_obj_t *img, std::vector<image::Image *> images) {
|
inline void lv_animimg_set_src(lv_obj_t *img, std::vector<image::Image *> images) {
|
||||||
@ -84,7 +94,9 @@ class LvCompound {
|
|||||||
lv_obj_t *obj{};
|
lv_obj_t *obj{};
|
||||||
};
|
};
|
||||||
|
|
||||||
class LvPageType {
|
class LvglComponent;
|
||||||
|
|
||||||
|
class LvPageType : public Parented<LvglComponent> {
|
||||||
public:
|
public:
|
||||||
LvPageType(bool skip) : skip(skip) {}
|
LvPageType(bool skip) : skip(skip) {}
|
||||||
|
|
||||||
@ -92,6 +104,9 @@ class LvPageType {
|
|||||||
this->index = index;
|
this->index = index;
|
||||||
this->obj = lv_obj_create(nullptr);
|
this->obj = lv_obj_create(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_showing() const;
|
||||||
|
|
||||||
lv_obj_t *obj{};
|
lv_obj_t *obj{};
|
||||||
size_t index{};
|
size_t index{};
|
||||||
bool skip;
|
bool skip;
|
||||||
@ -178,6 +193,7 @@ class LvglComponent : public PollingComponent {
|
|||||||
void show_next_page(lv_scr_load_anim_t anim, uint32_t time);
|
void show_next_page(lv_scr_load_anim_t anim, uint32_t time);
|
||||||
void show_prev_page(lv_scr_load_anim_t anim, uint32_t time);
|
void show_prev_page(lv_scr_load_anim_t anim, uint32_t time);
|
||||||
void set_page_wrap(bool wrap) { this->page_wrap_ = wrap; }
|
void set_page_wrap(bool wrap) { this->page_wrap_ = wrap; }
|
||||||
|
size_t get_current_page() const;
|
||||||
void set_focus_mark(lv_group_t *group) { this->focus_marks_[group] = lv_group_get_focused(group); }
|
void set_focus_mark(lv_group_t *group) { this->focus_marks_[group] = lv_group_get_focused(group); }
|
||||||
void restore_focus_mark(lv_group_t *group) {
|
void restore_focus_mark(lv_group_t *group) {
|
||||||
auto *mark = this->focus_marks_[group];
|
auto *mark = this->focus_marks_[group];
|
||||||
@ -241,14 +257,13 @@ template<typename... Ts> class LvglAction : public Action<Ts...>, public Parente
|
|||||||
std::function<void(LvglComponent *)> action_{};
|
std::function<void(LvglComponent *)> action_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts> class LvglCondition : public Condition<Ts...>, public Parented<LvglComponent> {
|
template<typename Tc, typename... Ts> class LvglCondition : public Condition<Ts...>, public Parented<Tc> {
|
||||||
public:
|
public:
|
||||||
LvglCondition(std::function<bool(LvglComponent *)> &&condition_lambda)
|
LvglCondition(std::function<bool(Tc *)> &&condition_lambda) : condition_lambda_(std::move(condition_lambda)) {}
|
||||||
: condition_lambda_(std::move(condition_lambda)) {}
|
|
||||||
bool check(Ts... x) override { return this->condition_lambda_(this->parent_); }
|
bool check(Ts... x) override { return this->condition_lambda_(this->parent_); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::function<bool(LvglComponent *)> condition_lambda_{};
|
std::function<bool(Tc *)> condition_lambda_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef USE_LVGL_TOUCHSCREEN
|
#ifdef USE_LVGL_TOUCHSCREEN
|
||||||
|
@ -19,7 +19,7 @@ from esphome.schema_extractors import SCHEMA_EXTRACT
|
|||||||
from . import defines as df, lv_validation as lvalid
|
from . import defines as df, lv_validation as lvalid
|
||||||
from .defines import CONF_TIME_FORMAT, LV_GRAD_DIR
|
from .defines import CONF_TIME_FORMAT, LV_GRAD_DIR
|
||||||
from .helpers import add_lv_use, requires_component, validate_printf
|
from .helpers import add_lv_use, requires_component, validate_printf
|
||||||
from .lv_validation import lv_color, lv_font, lv_gradient, lv_image
|
from .lv_validation import lv_color, lv_font, lv_gradient, lv_image, opacity
|
||||||
from .lvcode import LvglComponent, lv_event_t_ptr
|
from .lvcode import LvglComponent, lv_event_t_ptr
|
||||||
from .types import (
|
from .types import (
|
||||||
LVEncoderListener,
|
LVEncoderListener,
|
||||||
@ -344,8 +344,11 @@ FLEX_OBJ_SCHEMA = {
|
|||||||
|
|
||||||
DISP_BG_SCHEMA = cv.Schema(
|
DISP_BG_SCHEMA = cv.Schema(
|
||||||
{
|
{
|
||||||
cv.Optional(df.CONF_DISP_BG_IMAGE): lv_image,
|
cv.Optional(df.CONF_DISP_BG_IMAGE): cv.Any(
|
||||||
|
cv.one_of("none", lower=True), lv_image
|
||||||
|
),
|
||||||
cv.Optional(df.CONF_DISP_BG_COLOR): lv_color,
|
cv.Optional(df.CONF_DISP_BG_COLOR): lv_color,
|
||||||
|
cv.Optional(df.CONF_DISP_BG_OPA): opacity,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ from ..defines import (
|
|||||||
CONF_START_VALUE,
|
CONF_START_VALUE,
|
||||||
CONF_TICKS,
|
CONF_TICKS,
|
||||||
)
|
)
|
||||||
from ..helpers import add_lv_use
|
from ..helpers import add_lv_use, lvgl_components_required
|
||||||
from ..lv_validation import (
|
from ..lv_validation import (
|
||||||
angle,
|
angle,
|
||||||
get_end_value,
|
get_end_value,
|
||||||
@ -182,6 +182,7 @@ class MeterType(WidgetType):
|
|||||||
async def to_code(self, w: Widget, config):
|
async def to_code(self, w: Widget, config):
|
||||||
"""For a meter object, create and set parameters"""
|
"""For a meter object, create and set parameters"""
|
||||||
|
|
||||||
|
lvgl_components_required.add(CONF_METER)
|
||||||
var = w.obj
|
var = w.obj
|
||||||
for scale_conf in config.get(CONF_SCALES, ()):
|
for scale_conf in config.get(CONF_SCALES, ()):
|
||||||
rotation = 90 + (360 - scale_conf[CONF_ANGLE_RANGE]) / 2
|
rotation = 90 + (360 - scale_conf[CONF_ANGLE_RANGE]) / 2
|
||||||
|
@ -2,6 +2,7 @@ from esphome import automation, codegen as cg
|
|||||||
from esphome.automation import Trigger
|
from esphome.automation import Trigger
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID, CONF_PAGES, CONF_TIME, CONF_TRIGGER_ID
|
from esphome.const import CONF_ID, CONF_PAGES, CONF_TIME, CONF_TRIGGER_ID
|
||||||
|
from esphome.cpp_generator import MockObj, TemplateArguments
|
||||||
|
|
||||||
from ..defines import (
|
from ..defines import (
|
||||||
CONF_ANIMATION,
|
CONF_ANIMATION,
|
||||||
@ -17,18 +18,28 @@ from ..lvcode import (
|
|||||||
EVENT_ARG,
|
EVENT_ARG,
|
||||||
LVGL_COMP_ARG,
|
LVGL_COMP_ARG,
|
||||||
LambdaContext,
|
LambdaContext,
|
||||||
|
ReturnStatement,
|
||||||
add_line_marks,
|
add_line_marks,
|
||||||
lv_add,
|
lv_add,
|
||||||
lvgl_comp,
|
lvgl_comp,
|
||||||
lvgl_static,
|
lvgl_static,
|
||||||
)
|
)
|
||||||
from ..schemas import LVGL_SCHEMA
|
from ..schemas import LVGL_SCHEMA
|
||||||
from ..types import LvglAction, lv_page_t
|
from ..types import LvglAction, LvglCondition, lv_page_t
|
||||||
from . import Widget, WidgetType, add_widgets, get_widgets, set_obj_properties
|
from . import (
|
||||||
|
Widget,
|
||||||
|
WidgetType,
|
||||||
|
add_widgets,
|
||||||
|
get_widgets,
|
||||||
|
set_obj_properties,
|
||||||
|
wait_for_widgets,
|
||||||
|
)
|
||||||
|
|
||||||
CONF_ON_LOAD = "on_load"
|
CONF_ON_LOAD = "on_load"
|
||||||
CONF_ON_UNLOAD = "on_unload"
|
CONF_ON_UNLOAD = "on_unload"
|
||||||
|
|
||||||
|
PAGE_ARG = "_page"
|
||||||
|
|
||||||
PAGE_SCHEMA = cv.Schema(
|
PAGE_SCHEMA = cv.Schema(
|
||||||
{
|
{
|
||||||
cv.Optional(CONF_SKIP, default=False): lv_bool,
|
cv.Optional(CONF_SKIP, default=False): lv_bool,
|
||||||
@ -86,6 +97,30 @@ async def page_next_to_code(config, action_id, template_arg, args):
|
|||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_condition(
|
||||||
|
"lvgl.page.is_showing",
|
||||||
|
LvglCondition,
|
||||||
|
cv.maybe_simple_value(
|
||||||
|
cv.Schema({cv.Required(CONF_ID): cv.use_id(lv_page_t)}),
|
||||||
|
key=CONF_ID,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def page_is_showing_to_code(config, condition_id, template_arg, args):
|
||||||
|
await wait_for_widgets()
|
||||||
|
page = await cg.get_variable(config[CONF_ID])
|
||||||
|
async with LambdaContext(
|
||||||
|
[(lv_page_t.operator("ptr"), PAGE_ARG)], return_type=cg.bool_
|
||||||
|
) as context:
|
||||||
|
lv_add(ReturnStatement(MockObj(PAGE_ARG, "->").is_showing()))
|
||||||
|
var = cg.new_Pvariable(
|
||||||
|
condition_id,
|
||||||
|
TemplateArguments(lv_page_t, *template_arg),
|
||||||
|
await context.get_lambda(),
|
||||||
|
)
|
||||||
|
await cg.register_parented(var, page)
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
@automation.register_action(
|
@automation.register_action(
|
||||||
"lvgl.page.previous",
|
"lvgl.page.previous",
|
||||||
LvglAction,
|
LvglAction,
|
||||||
|
@ -11,16 +11,18 @@ void MicroNovaSwitch::write_state(bool state) {
|
|||||||
if (this->micronova_->get_current_stove_state() == 0) {
|
if (this->micronova_->get_current_stove_state() == 0) {
|
||||||
this->micronova_->write_address(this->memory_location_, this->memory_address_, this->memory_data_on_);
|
this->micronova_->write_address(this->memory_location_, this->memory_address_, this->memory_data_on_);
|
||||||
this->publish_state(true);
|
this->publish_state(true);
|
||||||
} else
|
} else {
|
||||||
ESP_LOGW(TAG, "Unable to turn stove on, invalid state: %d", micronova_->get_current_stove_state());
|
ESP_LOGW(TAG, "Unable to turn stove on, invalid state: %d", micronova_->get_current_stove_state());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// don't send power-off when status is Off or Final cleaning
|
// don't send power-off when status is Off or Final cleaning
|
||||||
if (this->micronova_->get_current_stove_state() != 0 && micronova_->get_current_stove_state() != 6) {
|
if (this->micronova_->get_current_stove_state() != 0 && micronova_->get_current_stove_state() != 6) {
|
||||||
this->micronova_->write_address(this->memory_location_, this->memory_address_, this->memory_data_off_);
|
this->micronova_->write_address(this->memory_location_, this->memory_address_, this->memory_data_off_);
|
||||||
this->publish_state(false);
|
this->publish_state(false);
|
||||||
} else
|
} else {
|
||||||
ESP_LOGW(TAG, "Unable to turn stove off, invalid state: %d", micronova_->get_current_stove_state());
|
ESP_LOGW(TAG, "Unable to turn stove off, invalid state: %d", micronova_->get_current_stove_state());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
this->micronova_->update();
|
this->micronova_->update();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import light, spi
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import light
|
from esphome.const import CONF_NUM_LEDS, CONF_OUTPUT_ID
|
||||||
from esphome.components import spi
|
|
||||||
from esphome.const import CONF_OUTPUT_ID, CONF_NUM_LEDS
|
|
||||||
|
|
||||||
spi_led_strip_ns = cg.esphome_ns.namespace("spi_led_strip")
|
spi_led_strip_ns = cg.esphome_ns.namespace("spi_led_strip")
|
||||||
SpiLedStrip = spi_led_strip_ns.class_(
|
SpiLedStrip = spi_led_strip_ns.class_(
|
||||||
@ -18,8 +17,7 @@ CONFIG_SCHEMA = light.ADDRESSABLE_LIGHT_SCHEMA.extend(
|
|||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
|
var = cg.new_Pvariable(config[CONF_OUTPUT_ID], config[CONF_NUM_LEDS])
|
||||||
cg.add(var.set_num_leds(config[CONF_NUM_LEDS]))
|
|
||||||
await light.register_light(var, config)
|
await light.register_light(var, config)
|
||||||
await spi.register_spi_device(var, config)
|
await spi.register_spi_device(var, config)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
67
esphome/components/spi_led_strip/spi_led_strip.cpp
Normal file
67
esphome/components/spi_led_strip/spi_led_strip.cpp
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#include "spi_led_strip.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace spi_led_strip {
|
||||||
|
|
||||||
|
SpiLedStrip::SpiLedStrip(uint16_t num_leds) {
|
||||||
|
this->num_leds_ = num_leds;
|
||||||
|
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
||||||
|
this->buffer_size_ = num_leds * 4 + 8;
|
||||||
|
this->buf_ = allocator.allocate(this->buffer_size_);
|
||||||
|
if (this->buf_ == nullptr) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate buffer of size %u", this->buffer_size_);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->effect_data_ = allocator.allocate(num_leds);
|
||||||
|
if (this->effect_data_ == nullptr) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate effect data of size %u", num_leds);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memset(this->buf_, 0xFF, this->buffer_size_);
|
||||||
|
memset(this->buf_, 0, 4);
|
||||||
|
}
|
||||||
|
void SpiLedStrip::setup() {
|
||||||
|
if (this->effect_data_ == nullptr || this->buf_ == nullptr) {
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->spi_setup();
|
||||||
|
}
|
||||||
|
light::LightTraits SpiLedStrip::get_traits() {
|
||||||
|
auto traits = light::LightTraits();
|
||||||
|
traits.set_supported_color_modes({light::ColorMode::RGB});
|
||||||
|
return traits;
|
||||||
|
}
|
||||||
|
void SpiLedStrip::dump_config() {
|
||||||
|
esph_log_config(TAG, "SPI LED Strip:");
|
||||||
|
esph_log_config(TAG, " LEDs: %d", this->num_leds_);
|
||||||
|
if (this->data_rate_ >= spi::DATA_RATE_1MHZ) {
|
||||||
|
esph_log_config(TAG, " Data rate: %uMHz", (unsigned) (this->data_rate_ / 1000000));
|
||||||
|
} else {
|
||||||
|
esph_log_config(TAG, " Data rate: %ukHz", (unsigned) (this->data_rate_ / 1000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void SpiLedStrip::write_state(light::LightState *state) {
|
||||||
|
if (this->is_failed())
|
||||||
|
return;
|
||||||
|
if (ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE) {
|
||||||
|
char strbuf[49];
|
||||||
|
size_t len = std::min(this->buffer_size_, (size_t) (sizeof(strbuf) - 1) / 3);
|
||||||
|
memset(strbuf, 0, sizeof(strbuf));
|
||||||
|
for (size_t i = 0; i != len; i++) {
|
||||||
|
sprintf(strbuf + i * 3, "%02X ", this->buf_[i]);
|
||||||
|
}
|
||||||
|
esph_log_v(TAG, "write_state: buf = %s", strbuf);
|
||||||
|
}
|
||||||
|
this->enable();
|
||||||
|
this->write_array(this->buf_, this->buffer_size_);
|
||||||
|
this->disable();
|
||||||
|
}
|
||||||
|
light::ESPColorView SpiLedStrip::get_view_internal(int32_t index) const {
|
||||||
|
size_t pos = index * 4 + 5;
|
||||||
|
return {this->buf_ + pos + 2, this->buf_ + pos + 1, this->buf_ + pos + 0, nullptr,
|
||||||
|
this->effect_data_ + index, &this->correction_};
|
||||||
|
}
|
||||||
|
} // namespace spi_led_strip
|
||||||
|
} // namespace esphome
|
@ -13,74 +13,22 @@ class SpiLedStrip : public light::AddressableLight,
|
|||||||
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_HIGH, spi::CLOCK_PHASE_TRAILING,
|
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_HIGH, spi::CLOCK_PHASE_TRAILING,
|
||||||
spi::DATA_RATE_1MHZ> {
|
spi::DATA_RATE_1MHZ> {
|
||||||
public:
|
public:
|
||||||
void setup() override { this->spi_setup(); }
|
SpiLedStrip(uint16_t num_leds);
|
||||||
|
void setup() override;
|
||||||
|
float get_setup_priority() const override { return setup_priority::IO; }
|
||||||
|
|
||||||
int32_t size() const override { return this->num_leds_; }
|
int32_t size() const override { return this->num_leds_; }
|
||||||
|
|
||||||
light::LightTraits get_traits() override {
|
light::LightTraits get_traits() override;
|
||||||
auto traits = light::LightTraits();
|
|
||||||
traits.set_supported_color_modes({light::ColorMode::RGB});
|
|
||||||
return traits;
|
|
||||||
}
|
|
||||||
void set_num_leds(uint16_t num_leds) {
|
|
||||||
this->num_leds_ = num_leds;
|
|
||||||
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
|
||||||
this->buffer_size_ = num_leds * 4 + 8;
|
|
||||||
this->buf_ = allocator.allocate(this->buffer_size_);
|
|
||||||
if (this->buf_ == nullptr) {
|
|
||||||
esph_log_e(TAG, "Failed to allocate buffer of size %u", this->buffer_size_);
|
|
||||||
this->mark_failed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->effect_data_ = allocator.allocate(num_leds);
|
void dump_config() override;
|
||||||
if (this->effect_data_ == nullptr) {
|
|
||||||
esph_log_e(TAG, "Failed to allocate effect data of size %u", num_leds);
|
|
||||||
this->mark_failed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
memset(this->buf_, 0xFF, this->buffer_size_);
|
|
||||||
memset(this->buf_, 0, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dump_config() override {
|
void write_state(light::LightState *state) override;
|
||||||
esph_log_config(TAG, "SPI LED Strip:");
|
|
||||||
esph_log_config(TAG, " LEDs: %d", this->num_leds_);
|
|
||||||
if (this->data_rate_ >= spi::DATA_RATE_1MHZ) {
|
|
||||||
esph_log_config(TAG, " Data rate: %uMHz", (unsigned) (this->data_rate_ / 1000000));
|
|
||||||
} else {
|
|
||||||
esph_log_config(TAG, " Data rate: %ukHz", (unsigned) (this->data_rate_ / 1000));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_state(light::LightState *state) override {
|
void clear_effect_data() override { memset(this->effect_data_, 0, this->num_leds_ * sizeof(this->effect_data_[0])); }
|
||||||
if (this->is_failed())
|
|
||||||
return;
|
|
||||||
if (ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE) {
|
|
||||||
char strbuf[49];
|
|
||||||
size_t len = std::min(this->buffer_size_, (size_t) (sizeof(strbuf) - 1) / 3);
|
|
||||||
memset(strbuf, 0, sizeof(strbuf));
|
|
||||||
for (size_t i = 0; i != len; i++) {
|
|
||||||
sprintf(strbuf + i * 3, "%02X ", this->buf_[i]);
|
|
||||||
}
|
|
||||||
esph_log_v(TAG, "write_state: buf = %s", strbuf);
|
|
||||||
}
|
|
||||||
this->enable();
|
|
||||||
this->write_array(this->buf_, this->buffer_size_);
|
|
||||||
this->disable();
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear_effect_data() override {
|
|
||||||
for (int i = 0; i < this->size(); i++)
|
|
||||||
this->effect_data_[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
light::ESPColorView get_view_internal(int32_t index) const override {
|
light::ESPColorView get_view_internal(int32_t index) const override;
|
||||||
size_t pos = index * 4 + 5;
|
|
||||||
return {this->buf_ + pos + 2, this->buf_ + pos + 1, this->buf_ + pos + 0, nullptr,
|
|
||||||
this->effect_data_ + index, &this->correction_};
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t buffer_size_{};
|
size_t buffer_size_{};
|
||||||
uint8_t *effect_data_{nullptr};
|
uint8_t *effect_data_{nullptr};
|
||||||
|
@ -184,11 +184,13 @@ void SprinklerValveOperator::set_controller(Sprinkler *controller) {
|
|||||||
|
|
||||||
void SprinklerValveOperator::set_valve(SprinklerValve *valve) {
|
void SprinklerValveOperator::set_valve(SprinklerValve *valve) {
|
||||||
if (valve != nullptr) {
|
if (valve != nullptr) {
|
||||||
|
if (this->state_ != IDLE) { // Only kill if not already idle
|
||||||
|
this->kill_(); // ensure everything is off before we let go!
|
||||||
|
}
|
||||||
this->state_ = IDLE; // reset state
|
this->state_ = IDLE; // reset state
|
||||||
this->run_duration_ = 0; // reset to ensure the valve isn't started without updating it
|
this->run_duration_ = 0; // reset to ensure the valve isn't started without updating it
|
||||||
this->start_millis_ = 0; // reset because (new) valve has not been started yet
|
this->start_millis_ = 0; // reset because (new) valve has not been started yet
|
||||||
this->stop_millis_ = 0; // reset because (new) valve has not been started yet
|
this->stop_millis_ = 0; // reset because (new) valve has not been started yet
|
||||||
this->kill_(); // ensure everything is off before we let go!
|
|
||||||
this->valve_ = valve; // finally, set the pointer to the new valve
|
this->valve_ = valve; // finally, set the pointer to the new valve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,8 +106,9 @@ void ToshibaClimate::setup() {
|
|||||||
this->publish_state();
|
this->publish_state();
|
||||||
});
|
});
|
||||||
this->current_temperature = this->sensor_->state;
|
this->current_temperature = this->sensor_->state;
|
||||||
} else
|
} else {
|
||||||
this->current_temperature = NAN;
|
this->current_temperature = NAN;
|
||||||
|
}
|
||||||
// restore set points
|
// restore set points
|
||||||
auto restore = this->restore_state_();
|
auto restore = this->restore_state_();
|
||||||
if (restore.has_value()) {
|
if (restore.has_value()) {
|
||||||
|
@ -120,8 +120,9 @@ light::LightTraits TuyaLight::get_traits() {
|
|||||||
traits.set_supported_color_modes(
|
traits.set_supported_color_modes(
|
||||||
{light::ColorMode::RGB_COLOR_TEMPERATURE, light::ColorMode::COLOR_TEMPERATURE});
|
{light::ColorMode::RGB_COLOR_TEMPERATURE, light::ColorMode::COLOR_TEMPERATURE});
|
||||||
}
|
}
|
||||||
} else
|
} else {
|
||||||
traits.set_supported_color_modes({light::ColorMode::COLOR_TEMPERATURE});
|
traits.set_supported_color_modes({light::ColorMode::COLOR_TEMPERATURE});
|
||||||
|
}
|
||||||
traits.set_min_mireds(this->cold_white_temperature_);
|
traits.set_min_mireds(this->cold_white_temperature_);
|
||||||
traits.set_max_mireds(this->warm_white_temperature_);
|
traits.set_max_mireds(this->warm_white_temperature_);
|
||||||
} else if (this->color_id_.has_value()) {
|
} else if (this->color_id_.has_value()) {
|
||||||
@ -131,8 +132,9 @@ light::LightTraits TuyaLight::get_traits() {
|
|||||||
} else {
|
} else {
|
||||||
traits.set_supported_color_modes({light::ColorMode::RGB_WHITE});
|
traits.set_supported_color_modes({light::ColorMode::RGB_WHITE});
|
||||||
}
|
}
|
||||||
} else
|
} else {
|
||||||
traits.set_supported_color_modes({light::ColorMode::RGB});
|
traits.set_supported_color_modes({light::ColorMode::RGB});
|
||||||
|
}
|
||||||
} else if (this->dimmer_id_.has_value()) {
|
} else if (this->dimmer_id_.has_value()) {
|
||||||
traits.set_supported_color_modes({light::ColorMode::BRIGHTNESS});
|
traits.set_supported_color_modes({light::ColorMode::BRIGHTNESS});
|
||||||
} else {
|
} else {
|
||||||
|
@ -85,7 +85,7 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
cv.GenerateID(): cv.declare_id(UDPComponent),
|
cv.GenerateID(): cv.declare_id(UDPComponent),
|
||||||
cv.Optional(CONF_PORT, default=18511): cv.port,
|
cv.Optional(CONF_PORT, default=18511): cv.port,
|
||||||
cv.Optional(CONF_ADDRESSES, default=["255.255.255.255"]): cv.ensure_list(
|
cv.Optional(CONF_ADDRESSES, default=["255.255.255.255"]): cv.ensure_list(
|
||||||
cv.ipv4
|
cv.ipv4address,
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_ROLLING_CODE_ENABLE, default=False): cv.boolean,
|
cv.Optional(CONF_ROLLING_CODE_ENABLE, default=False): cv.boolean,
|
||||||
cv.Optional(CONF_PING_PONG_ENABLE, default=False): cv.boolean,
|
cv.Optional(CONF_PING_PONG_ENABLE, default=False): cv.boolean,
|
||||||
|
@ -245,13 +245,9 @@ void UDPComponent::setup() {
|
|||||||
}
|
}
|
||||||
struct sockaddr_in server {};
|
struct sockaddr_in server {};
|
||||||
|
|
||||||
socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), this->port_);
|
server.sin_family = AF_INET;
|
||||||
if (sl == 0) {
|
server.sin_addr.s_addr = ESPHOME_INADDR_ANY;
|
||||||
ESP_LOGE(TAG, "Socket unable to set sockaddr: errno %d", errno);
|
server.sin_port = htons(this->port_);
|
||||||
this->mark_failed();
|
|
||||||
this->status_set_error("Unable to set sockaddr");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = this->listen_socket_->bind((struct sockaddr *) &server, sizeof(server));
|
err = this->listen_socket_->bind((struct sockaddr *) &server, sizeof(server));
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
|
@ -93,16 +93,16 @@ def validate_channel(value):
|
|||||||
|
|
||||||
AP_MANUAL_IP_SCHEMA = cv.Schema(
|
AP_MANUAL_IP_SCHEMA = cv.Schema(
|
||||||
{
|
{
|
||||||
cv.Required(CONF_STATIC_IP): cv.ipv4,
|
cv.Required(CONF_STATIC_IP): cv.ipv4address,
|
||||||
cv.Required(CONF_GATEWAY): cv.ipv4,
|
cv.Required(CONF_GATEWAY): cv.ipv4address,
|
||||||
cv.Required(CONF_SUBNET): cv.ipv4,
|
cv.Required(CONF_SUBNET): cv.ipv4address,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
STA_MANUAL_IP_SCHEMA = AP_MANUAL_IP_SCHEMA.extend(
|
STA_MANUAL_IP_SCHEMA = AP_MANUAL_IP_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
cv.Optional(CONF_DNS1, default="0.0.0.0"): cv.ipv4,
|
cv.Optional(CONF_DNS1, default="0.0.0.0"): cv.ipv4address,
|
||||||
cv.Optional(CONF_DNS2, default="0.0.0.0"): cv.ipv4,
|
cv.Optional(CONF_DNS2, default="0.0.0.0"): cv.ipv4address,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -364,7 +364,7 @@ def eap_auth(config):
|
|||||||
def safe_ip(ip):
|
def safe_ip(ip):
|
||||||
if ip is None:
|
if ip is None:
|
||||||
return IPAddress(0, 0, 0, 0)
|
return IPAddress(0, 0, 0, 0)
|
||||||
return IPAddress(*ip.args)
|
return IPAddress(str(ip))
|
||||||
|
|
||||||
|
|
||||||
def manual_ip(config):
|
def manual_ip(config):
|
||||||
|
@ -67,8 +67,8 @@ CONFIG_SCHEMA = cv.Schema(
|
|||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(Wireguard),
|
cv.GenerateID(): cv.declare_id(Wireguard),
|
||||||
cv.GenerateID(CONF_TIME_ID): cv.use_id(time.RealTimeClock),
|
cv.GenerateID(CONF_TIME_ID): cv.use_id(time.RealTimeClock),
|
||||||
cv.Required(CONF_ADDRESS): cv.ipv4,
|
cv.Required(CONF_ADDRESS): cv.ipv4address,
|
||||||
cv.Optional(CONF_NETMASK, default="255.255.255.255"): cv.ipv4,
|
cv.Optional(CONF_NETMASK, default="255.255.255.255"): cv.ipv4address,
|
||||||
cv.Required(CONF_PRIVATE_KEY): _wireguard_key,
|
cv.Required(CONF_PRIVATE_KEY): _wireguard_key,
|
||||||
cv.Required(CONF_PEER_ENDPOINT): cv.string,
|
cv.Required(CONF_PEER_ENDPOINT): cv.string,
|
||||||
cv.Required(CONF_PEER_PUBLIC_KEY): _wireguard_key,
|
cv.Required(CONF_PEER_PUBLIC_KEY): _wireguard_key,
|
||||||
|
@ -104,8 +104,9 @@ void YashimaClimate::setup() {
|
|||||||
this->publish_state();
|
this->publish_state();
|
||||||
});
|
});
|
||||||
this->current_temperature = this->sensor_->state;
|
this->current_temperature = this->sensor_->state;
|
||||||
} else
|
} else {
|
||||||
this->current_temperature = NAN;
|
this->current_temperature = NAN;
|
||||||
|
}
|
||||||
// restore set points
|
// restore set points
|
||||||
auto restore = this->restore_state_();
|
auto restore = this->restore_state_();
|
||||||
if (restore.has_value()) {
|
if (restore.has_value()) {
|
||||||
|
@ -18,6 +18,7 @@ from esphome.const import (
|
|||||||
CONF_ESPHOME,
|
CONF_ESPHOME,
|
||||||
CONF_EXTERNAL_COMPONENTS,
|
CONF_EXTERNAL_COMPONENTS,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
|
CONF_MIN_VERSION,
|
||||||
CONF_PACKAGES,
|
CONF_PACKAGES,
|
||||||
CONF_PLATFORM,
|
CONF_PLATFORM,
|
||||||
CONF_SUBSTITUTIONS,
|
CONF_SUBSTITUTIONS,
|
||||||
@ -839,6 +840,10 @@ def validate_config(
|
|||||||
# Remove temporary esphome config path again, it will be reloaded later
|
# Remove temporary esphome config path again, it will be reloaded later
|
||||||
result.remove_output_path([CONF_ESPHOME], CONF_ESPHOME)
|
result.remove_output_path([CONF_ESPHOME], CONF_ESPHOME)
|
||||||
|
|
||||||
|
# Check version number now to avoid loading components that are not supported
|
||||||
|
if min_version := config[CONF_ESPHOME].get(CONF_MIN_VERSION):
|
||||||
|
cv.All(cv.version_number, cv.validate_esphome_version)(min_version)
|
||||||
|
|
||||||
# First run platform validation steps
|
# First run platform validation steps
|
||||||
for key in TARGET_PLATFORMS:
|
for key in TARGET_PLATFORMS:
|
||||||
if key in config:
|
if key in config:
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from ipaddress import AddressValueError, IPv4Address, ip_address
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@ -67,7 +68,6 @@ from esphome.const import (
|
|||||||
from esphome.core import (
|
from esphome.core import (
|
||||||
CORE,
|
CORE,
|
||||||
HexInt,
|
HexInt,
|
||||||
IPAddress,
|
|
||||||
Lambda,
|
Lambda,
|
||||||
TimePeriod,
|
TimePeriod,
|
||||||
TimePeriodMicroseconds,
|
TimePeriodMicroseconds,
|
||||||
@ -1130,7 +1130,7 @@ def domain(value):
|
|||||||
if re.match(vol.DOMAIN_REGEX, value) is not None:
|
if re.match(vol.DOMAIN_REGEX, value) is not None:
|
||||||
return value
|
return value
|
||||||
try:
|
try:
|
||||||
return str(ipv4(value))
|
return str(ipaddress(value))
|
||||||
except Invalid as err:
|
except Invalid as err:
|
||||||
raise Invalid(f"Invalid domain: {value}") from err
|
raise Invalid(f"Invalid domain: {value}") from err
|
||||||
|
|
||||||
@ -1160,21 +1160,20 @@ def ssid(value):
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
def ipv4(value):
|
def ipv4address(value):
|
||||||
if isinstance(value, list):
|
try:
|
||||||
parts = value
|
address = IPv4Address(value)
|
||||||
elif isinstance(value, str):
|
except AddressValueError as exc:
|
||||||
parts = value.split(".")
|
raise Invalid(f"{value} is not a valid IPv4 address") from exc
|
||||||
elif isinstance(value, IPAddress):
|
return address
|
||||||
return value
|
|
||||||
else:
|
|
||||||
raise Invalid("IPv4 address must consist of either string or integer list")
|
def ipaddress(value):
|
||||||
if len(parts) != 4:
|
try:
|
||||||
raise Invalid("IPv4 address must consist of four point-separated integers")
|
address = ip_address(value)
|
||||||
parts_ = list(map(int, parts))
|
except ValueError as exc:
|
||||||
if not all(0 <= x < 256 for x in parts_):
|
raise Invalid(f"{value} is not a valid IP address") from exc
|
||||||
raise Invalid("IPv4 address parts must be in range from 0 to 255")
|
return address
|
||||||
return IPAddress(*parts_)
|
|
||||||
|
|
||||||
|
|
||||||
def _valid_topic(value):
|
def _valid_topic(value):
|
||||||
|
@ -54,16 +54,6 @@ class HexInt(int):
|
|||||||
return f"{sign}0x{value:X}"
|
return f"{sign}0x{value:X}"
|
||||||
|
|
||||||
|
|
||||||
class IPAddress:
|
|
||||||
def __init__(self, *args):
|
|
||||||
if len(args) != 4:
|
|
||||||
raise ValueError("IPAddress must consist of 4 items")
|
|
||||||
self.args = args
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return ".".join(str(x) for x in self.args)
|
|
||||||
|
|
||||||
|
|
||||||
class MACAddress:
|
class MACAddress:
|
||||||
def __init__(self, *parts):
|
def __init__(self, *parts):
|
||||||
if len(parts) != 6:
|
if len(parts) != 6:
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
#define USE_LVGL_IMAGE
|
#define USE_LVGL_IMAGE
|
||||||
#define USE_LVGL_KEY_LISTENER
|
#define USE_LVGL_KEY_LISTENER
|
||||||
#define USE_LVGL_KEYBOARD
|
#define USE_LVGL_KEYBOARD
|
||||||
|
#define USE_LVGL_METER
|
||||||
#define USE_LVGL_ROLLER
|
#define USE_LVGL_ROLLER
|
||||||
#define USE_LVGL_ROTARY_ENCODER
|
#define USE_LVGL_ROTARY_ENCODER
|
||||||
#define USE_LVGL_TOUCHSCREEN
|
#define USE_LVGL_TOUCHSCREEN
|
||||||
|
@ -126,6 +126,7 @@ uint16_t crc16(const uint8_t *data, uint16_t len, uint16_t crc, uint16_t reverse
|
|||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
|
{
|
||||||
if (reverse_poly == 0xa001) {
|
if (reverse_poly == 0xa001) {
|
||||||
while (len--) {
|
while (len--) {
|
||||||
uint8_t combo = crc ^ (uint8_t) *data++;
|
uint8_t combo = crc ^ (uint8_t) *data++;
|
||||||
@ -143,6 +144,7 @@ uint16_t crc16(const uint8_t *data, uint16_t len, uint16_t crc, uint16_t reverse
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return refout ? (crc ^ 0xffff) : crc;
|
return refout ? (crc ^ 0xffff) : crc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,20 +67,18 @@ class ESPHomeLogFormatter(logging.Formatter):
|
|||||||
|
|
||||||
|
|
||||||
def setup_log(
|
def setup_log(
|
||||||
debug: bool = False, quiet: bool = False, include_timestamp: bool = False
|
log_level=logging.INFO,
|
||||||
|
include_timestamp: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
import colorama
|
import colorama
|
||||||
|
|
||||||
colorama.init()
|
colorama.init()
|
||||||
|
|
||||||
if debug:
|
if log_level == logging.DEBUG:
|
||||||
log_level = logging.DEBUG
|
|
||||||
CORE.verbose = True
|
CORE.verbose = True
|
||||||
elif quiet:
|
elif log_level == logging.CRITICAL:
|
||||||
log_level = logging.CRITICAL
|
|
||||||
CORE.quiet = True
|
CORE.quiet = True
|
||||||
else:
|
|
||||||
log_level = logging.INFO
|
|
||||||
logging.basicConfig(level=log_level)
|
logging.basicConfig(level=log_level)
|
||||||
|
|
||||||
logging.getLogger("urllib3").setLevel(logging.WARNING)
|
logging.getLogger("urllib3").setLevel(logging.WARNING)
|
||||||
|
@ -4,6 +4,7 @@ import fnmatch
|
|||||||
import functools
|
import functools
|
||||||
import inspect
|
import inspect
|
||||||
from io import TextIOWrapper
|
from io import TextIOWrapper
|
||||||
|
from ipaddress import _BaseAddress
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
@ -25,7 +26,6 @@ from esphome.core import (
|
|||||||
CORE,
|
CORE,
|
||||||
DocumentRange,
|
DocumentRange,
|
||||||
EsphomeError,
|
EsphomeError,
|
||||||
IPAddress,
|
|
||||||
Lambda,
|
Lambda,
|
||||||
MACAddress,
|
MACAddress,
|
||||||
TimePeriod,
|
TimePeriod,
|
||||||
@ -576,7 +576,7 @@ ESPHomeDumper.add_multi_representer(bool, ESPHomeDumper.represent_bool)
|
|||||||
ESPHomeDumper.add_multi_representer(str, ESPHomeDumper.represent_stringify)
|
ESPHomeDumper.add_multi_representer(str, ESPHomeDumper.represent_stringify)
|
||||||
ESPHomeDumper.add_multi_representer(int, ESPHomeDumper.represent_int)
|
ESPHomeDumper.add_multi_representer(int, ESPHomeDumper.represent_int)
|
||||||
ESPHomeDumper.add_multi_representer(float, ESPHomeDumper.represent_float)
|
ESPHomeDumper.add_multi_representer(float, ESPHomeDumper.represent_float)
|
||||||
ESPHomeDumper.add_multi_representer(IPAddress, ESPHomeDumper.represent_stringify)
|
ESPHomeDumper.add_multi_representer(_BaseAddress, ESPHomeDumper.represent_stringify)
|
||||||
ESPHomeDumper.add_multi_representer(MACAddress, ESPHomeDumper.represent_stringify)
|
ESPHomeDumper.add_multi_representer(MACAddress, ESPHomeDumper.represent_stringify)
|
||||||
ESPHomeDumper.add_multi_representer(TimePeriod, ESPHomeDumper.represent_stringify)
|
ESPHomeDumper.add_multi_representer(TimePeriod, ESPHomeDumper.represent_stringify)
|
||||||
ESPHomeDumper.add_multi_representer(Lambda, ESPHomeDumper.represent_lambda)
|
ESPHomeDumper.add_multi_representer(Lambda, ESPHomeDumper.represent_lambda)
|
||||||
|
@ -27,6 +27,7 @@ lvgl:
|
|||||||
bg_color: light_blue
|
bg_color: light_blue
|
||||||
disp_bg_color: color_id
|
disp_bg_color: color_id
|
||||||
disp_bg_image: cat_image
|
disp_bg_image: cat_image
|
||||||
|
disp_bg_opa: cover
|
||||||
theme:
|
theme:
|
||||||
obj:
|
obj:
|
||||||
border_width: 1
|
border_width: 1
|
||||||
@ -132,10 +133,16 @@ lvgl:
|
|||||||
|
|
||||||
pages:
|
pages:
|
||||||
- id: page1
|
- id: page1
|
||||||
|
bg_image_src: cat_image
|
||||||
on_load:
|
on_load:
|
||||||
- logger.log: page loaded
|
- logger.log: page loaded
|
||||||
- lvgl.widget.focus:
|
- lvgl.widget.focus:
|
||||||
action: restore
|
action: restore
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lvgl.page.is_showing: page1
|
||||||
|
then:
|
||||||
|
logger.log: "Yes, page1 showing"
|
||||||
on_unload:
|
on_unload:
|
||||||
- logger.log: page unloaded
|
- logger.log: page unloaded
|
||||||
- lvgl.widget.focus: mark
|
- lvgl.widget.focus: mark
|
||||||
@ -206,7 +213,7 @@ lvgl:
|
|||||||
- lvgl.animimg.stop: anim_img
|
- lvgl.animimg.stop: anim_img
|
||||||
- lvgl.update:
|
- lvgl.update:
|
||||||
disp_bg_color: 0xffff00
|
disp_bg_color: 0xffff00
|
||||||
disp_bg_image: cat_image
|
disp_bg_image: none
|
||||||
- lvgl.widget.show: message_box
|
- lvgl.widget.show: message_box
|
||||||
- label:
|
- label:
|
||||||
text: "Hello shiny day"
|
text: "Hello shiny day"
|
||||||
|
@ -7,7 +7,6 @@ display:
|
|||||||
height: 320
|
height: 320
|
||||||
- platform: sdl
|
- platform: sdl
|
||||||
id: sdl1
|
id: sdl1
|
||||||
auto_clear_enabled: false
|
|
||||||
dimensions:
|
dimensions:
|
||||||
width: 480
|
width: 480
|
||||||
height: 480
|
height: 480
|
||||||
@ -40,4 +39,3 @@ lvgl:
|
|||||||
text: Click ME
|
text: Click ME
|
||||||
on_click:
|
on_click:
|
||||||
logger.log: Clicked
|
logger.log: Clicked
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import pytest
|
|
||||||
import string
|
import string
|
||||||
|
|
||||||
from hypothesis import given, example
|
from hypothesis import example, given
|
||||||
from hypothesis.strategies import one_of, text, integers, builds
|
from hypothesis.strategies import builds, integers, ip_addresses, one_of, text
|
||||||
|
import pytest
|
||||||
|
|
||||||
from esphome import config_validation
|
from esphome import config_validation
|
||||||
from esphome.config_validation import Invalid
|
from esphome.config_validation import Invalid
|
||||||
from esphome.core import CORE, Lambda, HexInt
|
from esphome.core import CORE, HexInt, Lambda
|
||||||
|
|
||||||
|
|
||||||
def test_check_not_templatable__invalid():
|
def test_check_not_templatable__invalid():
|
||||||
@ -145,6 +145,28 @@ def test_boolean__invalid(value):
|
|||||||
config_validation.boolean(value)
|
config_validation.boolean(value)
|
||||||
|
|
||||||
|
|
||||||
|
@given(value=ip_addresses(v=4).map(str))
|
||||||
|
def test_ipv4__valid(value):
|
||||||
|
config_validation.ipv4address(value)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("value", ("127.0.0", "localhost", ""))
|
||||||
|
def test_ipv4__invalid(value):
|
||||||
|
with pytest.raises(Invalid, match="is not a valid IPv4 address"):
|
||||||
|
config_validation.ipv4address(value)
|
||||||
|
|
||||||
|
|
||||||
|
@given(value=ip_addresses(v=6).map(str))
|
||||||
|
def test_ipv6__valid(value):
|
||||||
|
config_validation.ipaddress(value)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("value", ("127.0.0", "localhost", "", "2001:db8::2::3"))
|
||||||
|
def test_ipv6__invalid(value):
|
||||||
|
with pytest.raises(Invalid, match="is not a valid IP address"):
|
||||||
|
config_validation.ipaddress(value)
|
||||||
|
|
||||||
|
|
||||||
# TODO: ensure_list
|
# TODO: ensure_list
|
||||||
@given(integers())
|
@given(integers())
|
||||||
def hex_int__valid(value):
|
def hex_int__valid(value):
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
import pytest
|
|
||||||
|
|
||||||
from hypothesis import given
|
from hypothesis import given
|
||||||
from hypothesis.strategies import ip_addresses
|
import pytest
|
||||||
from strategies import mac_addr_strings
|
from strategies import mac_addr_strings
|
||||||
|
|
||||||
from esphome import core, const
|
from esphome import const, core
|
||||||
|
|
||||||
|
|
||||||
class TestHexInt:
|
class TestHexInt:
|
||||||
@ -26,25 +24,6 @@ class TestHexInt:
|
|||||||
assert actual == expected
|
assert actual == expected
|
||||||
|
|
||||||
|
|
||||||
class TestIPAddress:
|
|
||||||
@given(value=ip_addresses(v=4).map(str))
|
|
||||||
def test_init__valid(self, value):
|
|
||||||
core.IPAddress(*value.split("."))
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("value", ("127.0.0", "localhost", ""))
|
|
||||||
def test_init__invalid(self, value):
|
|
||||||
with pytest.raises(ValueError, match="IPAddress must consist of 4 items"):
|
|
||||||
core.IPAddress(*value.split("."))
|
|
||||||
|
|
||||||
@given(value=ip_addresses(v=4).map(str))
|
|
||||||
def test_str(self, value):
|
|
||||||
target = core.IPAddress(*value.split("."))
|
|
||||||
|
|
||||||
actual = str(target)
|
|
||||||
|
|
||||||
assert actual == value
|
|
||||||
|
|
||||||
|
|
||||||
class TestMACAddress:
|
class TestMACAddress:
|
||||||
@given(value=mac_addr_strings())
|
@given(value=mac_addr_strings())
|
||||||
def test_init__valid(self, value):
|
def test_init__valid(self, value):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user