mirror of
https://github.com/esphome/esphome.git
synced 2025-09-02 03:12:20 +01:00
Merge branch 'integration' into memory_api
This commit is contained in:
@@ -11,7 +11,7 @@ ci:
|
|||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
# Ruff version.
|
# Ruff version.
|
||||||
rev: v0.12.10
|
rev: v0.12.11
|
||||||
hooks:
|
hooks:
|
||||||
# Run the linter.
|
# Run the linter.
|
||||||
- id: ruff
|
- id: ruff
|
||||||
|
@@ -61,11 +61,10 @@ void AbsoluteHumidityComponent::loop() {
|
|||||||
ESP_LOGW(TAG, "No valid state from temperature sensor!");
|
ESP_LOGW(TAG, "No valid state from temperature sensor!");
|
||||||
}
|
}
|
||||||
if (no_humidity) {
|
if (no_humidity) {
|
||||||
ESP_LOGW(TAG, "No valid state from temperature sensor!");
|
ESP_LOGW(TAG, "No valid state from humidity sensor!");
|
||||||
}
|
}
|
||||||
ESP_LOGW(TAG, "Unable to calculate absolute humidity.");
|
|
||||||
this->publish_state(NAN);
|
this->publish_state(NAN);
|
||||||
this->status_set_warning();
|
this->status_set_warning("Unable to calculate absolute humidity.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,9 +86,8 @@ void AbsoluteHumidityComponent::loop() {
|
|||||||
es = es_wobus(temperature_c);
|
es = es_wobus(temperature_c);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ESP_LOGE(TAG, "Invalid saturation vapor pressure equation selection!");
|
|
||||||
this->publish_state(NAN);
|
this->publish_state(NAN);
|
||||||
this->status_set_error();
|
this->status_set_error("Invalid saturation vapor pressure equation selection!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ESP_LOGD(TAG, "Saturation vapor pressure %f kPa", es);
|
ESP_LOGD(TAG, "Saturation vapor pressure %f kPa", es);
|
||||||
|
@@ -1712,6 +1712,7 @@ message BluetoothScannerStateResponse {
|
|||||||
|
|
||||||
BluetoothScannerState state = 1;
|
BluetoothScannerState state = 1;
|
||||||
BluetoothScannerMode mode = 2;
|
BluetoothScannerMode mode = 2;
|
||||||
|
BluetoothScannerMode configured_mode = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message BluetoothScannerSetModeRequest {
|
message BluetoothScannerSetModeRequest {
|
||||||
|
@@ -2153,10 +2153,12 @@ void BluetoothDeviceClearCacheResponse::calculate_size(ProtoSize &size) const {
|
|||||||
void BluetoothScannerStateResponse::encode(ProtoWriteBuffer buffer) const {
|
void BluetoothScannerStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_uint32(1, static_cast<uint32_t>(this->state));
|
buffer.encode_uint32(1, static_cast<uint32_t>(this->state));
|
||||||
buffer.encode_uint32(2, static_cast<uint32_t>(this->mode));
|
buffer.encode_uint32(2, static_cast<uint32_t>(this->mode));
|
||||||
|
buffer.encode_uint32(3, static_cast<uint32_t>(this->configured_mode));
|
||||||
}
|
}
|
||||||
void BluetoothScannerStateResponse::calculate_size(ProtoSize &size) const {
|
void BluetoothScannerStateResponse::calculate_size(ProtoSize &size) const {
|
||||||
size.add_uint32(1, static_cast<uint32_t>(this->state));
|
size.add_uint32(1, static_cast<uint32_t>(this->state));
|
||||||
size.add_uint32(1, static_cast<uint32_t>(this->mode));
|
size.add_uint32(1, static_cast<uint32_t>(this->mode));
|
||||||
|
size.add_uint32(1, static_cast<uint32_t>(this->configured_mode));
|
||||||
}
|
}
|
||||||
bool BluetoothScannerSetModeRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
bool BluetoothScannerSetModeRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
switch (field_id) {
|
switch (field_id) {
|
||||||
|
@@ -2214,12 +2214,13 @@ class BluetoothDeviceClearCacheResponse final : public ProtoMessage {
|
|||||||
class BluetoothScannerStateResponse final : public ProtoMessage {
|
class BluetoothScannerStateResponse final : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 126;
|
static constexpr uint8_t MESSAGE_TYPE = 126;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 4;
|
static constexpr uint8_t ESTIMATED_SIZE = 6;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "bluetooth_scanner_state_response"; }
|
const char *message_name() const override { return "bluetooth_scanner_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
enums::BluetoothScannerState state{};
|
enums::BluetoothScannerState state{};
|
||||||
enums::BluetoothScannerMode mode{};
|
enums::BluetoothScannerMode mode{};
|
||||||
|
enums::BluetoothScannerMode configured_mode{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(ProtoSize &size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
@@ -1704,6 +1704,7 @@ void BluetoothScannerStateResponse::dump_to(std::string &out) const {
|
|||||||
MessageDumpHelper helper(out, "BluetoothScannerStateResponse");
|
MessageDumpHelper helper(out, "BluetoothScannerStateResponse");
|
||||||
dump_field(out, "state", static_cast<enums::BluetoothScannerState>(this->state));
|
dump_field(out, "state", static_cast<enums::BluetoothScannerState>(this->state));
|
||||||
dump_field(out, "mode", static_cast<enums::BluetoothScannerMode>(this->mode));
|
dump_field(out, "mode", static_cast<enums::BluetoothScannerMode>(this->mode));
|
||||||
|
dump_field(out, "configured_mode", static_cast<enums::BluetoothScannerMode>(this->configured_mode));
|
||||||
}
|
}
|
||||||
void BluetoothScannerSetModeRequest::dump_to(std::string &out) const {
|
void BluetoothScannerSetModeRequest::dump_to(std::string &out) const {
|
||||||
MessageDumpHelper helper(out, "BluetoothScannerSetModeRequest");
|
MessageDumpHelper helper(out, "BluetoothScannerSetModeRequest");
|
||||||
|
@@ -24,6 +24,9 @@ void BluetoothProxy::setup() {
|
|||||||
this->connections_free_response_.limit = BLUETOOTH_PROXY_MAX_CONNECTIONS;
|
this->connections_free_response_.limit = BLUETOOTH_PROXY_MAX_CONNECTIONS;
|
||||||
this->connections_free_response_.free = BLUETOOTH_PROXY_MAX_CONNECTIONS;
|
this->connections_free_response_.free = BLUETOOTH_PROXY_MAX_CONNECTIONS;
|
||||||
|
|
||||||
|
// Capture the configured scan mode from YAML before any API changes
|
||||||
|
this->configured_scan_active_ = this->parent_->get_scan_active();
|
||||||
|
|
||||||
this->parent_->add_scanner_state_callback([this](esp32_ble_tracker::ScannerState state) {
|
this->parent_->add_scanner_state_callback([this](esp32_ble_tracker::ScannerState state) {
|
||||||
if (this->api_connection_ != nullptr) {
|
if (this->api_connection_ != nullptr) {
|
||||||
this->send_bluetooth_scanner_state_(state);
|
this->send_bluetooth_scanner_state_(state);
|
||||||
@@ -36,6 +39,9 @@ void BluetoothProxy::send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerSta
|
|||||||
resp.state = static_cast<api::enums::BluetoothScannerState>(state);
|
resp.state = static_cast<api::enums::BluetoothScannerState>(state);
|
||||||
resp.mode = this->parent_->get_scan_active() ? api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE
|
resp.mode = this->parent_->get_scan_active() ? api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE
|
||||||
: api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_PASSIVE;
|
: api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_PASSIVE;
|
||||||
|
resp.configured_mode = this->configured_scan_active_
|
||||||
|
? api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE
|
||||||
|
: api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_PASSIVE;
|
||||||
this->api_connection_->send_message(resp, api::BluetoothScannerStateResponse::MESSAGE_TYPE);
|
this->api_connection_->send_message(resp, api::BluetoothScannerStateResponse::MESSAGE_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -161,7 +161,8 @@ class BluetoothProxy final : public esp32_ble_tracker::ESPBTDeviceListener, publ
|
|||||||
// Group 4: 1-byte types grouped together
|
// Group 4: 1-byte types grouped together
|
||||||
bool active_;
|
bool active_;
|
||||||
uint8_t connection_count_{0};
|
uint8_t connection_count_{0};
|
||||||
// 2 bytes used, 2 bytes padding
|
bool configured_scan_active_{false}; // Configured scan mode from YAML
|
||||||
|
// 3 bytes used, 1 byte padding
|
||||||
};
|
};
|
||||||
|
|
||||||
extern BluetoothProxy *global_bluetooth_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
extern BluetoothProxy *global_bluetooth_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
@@ -451,6 +451,7 @@ CONF_GRID_ROWS = "grid_rows"
|
|||||||
CONF_HEADER_MODE = "header_mode"
|
CONF_HEADER_MODE = "header_mode"
|
||||||
CONF_HOME = "home"
|
CONF_HOME = "home"
|
||||||
CONF_INITIAL_FOCUS = "initial_focus"
|
CONF_INITIAL_FOCUS = "initial_focus"
|
||||||
|
CONF_SELECTED_DIGIT = "selected_digit"
|
||||||
CONF_KEY_CODE = "key_code"
|
CONF_KEY_CODE = "key_code"
|
||||||
CONF_KEYPADS = "keypads"
|
CONF_KEYPADS = "keypads"
|
||||||
CONF_LAYOUT = "layout"
|
CONF_LAYOUT = "layout"
|
||||||
|
@@ -4,41 +4,31 @@ from esphome.yaml_util import parse_yaml
|
|||||||
|
|
||||||
CONFIG = """
|
CONFIG = """
|
||||||
- obj:
|
- obj:
|
||||||
radius: 0
|
id: hello_world_card_
|
||||||
pad_all: 12
|
pad_all: 12
|
||||||
bg_color: 0xFFFFFF
|
bg_color: white
|
||||||
height: 100%
|
height: 100%
|
||||||
width: 100%
|
width: 100%
|
||||||
|
scrollable: false
|
||||||
|
widgets:
|
||||||
|
- obj:
|
||||||
|
align: top_mid
|
||||||
|
outline_width: 0
|
||||||
|
border_width: 0
|
||||||
|
pad_all: 4
|
||||||
|
scrollable: false
|
||||||
|
height: size_content
|
||||||
|
width: 100%
|
||||||
|
layout:
|
||||||
|
type: flex
|
||||||
|
flex_flow: row
|
||||||
|
flex_align_cross: center
|
||||||
|
flex_align_track: start
|
||||||
|
flex_align_main: space_between
|
||||||
widgets:
|
widgets:
|
||||||
- spinner:
|
|
||||||
id: hello_world_spinner_
|
|
||||||
align: center
|
|
||||||
indicator:
|
|
||||||
arc_color: tomato
|
|
||||||
height: 100
|
|
||||||
width: 100
|
|
||||||
spin_time: 2s
|
|
||||||
arc_length: 60deg
|
|
||||||
- label:
|
|
||||||
id: hello_world_label_
|
|
||||||
text: "Hello World!"
|
|
||||||
align: center
|
|
||||||
on_click:
|
|
||||||
lvgl.spinner.update:
|
|
||||||
id: hello_world_spinner_
|
|
||||||
arc_color: springgreen
|
|
||||||
- checkbox:
|
|
||||||
pad_all: 8
|
|
||||||
text: Checkbox
|
|
||||||
align: top_right
|
|
||||||
on_click:
|
|
||||||
lvgl.label.update:
|
|
||||||
id: hello_world_label_
|
|
||||||
text: "Checked!"
|
|
||||||
- button:
|
- button:
|
||||||
pad_all: 8
|
|
||||||
checkable: true
|
checkable: true
|
||||||
align: top_left
|
radius: 4
|
||||||
text_font: montserrat_20
|
text_font: montserrat_20
|
||||||
on_click:
|
on_click:
|
||||||
lvgl.label.update:
|
lvgl.label.update:
|
||||||
@@ -47,6 +37,79 @@ CONFIG = """
|
|||||||
widgets:
|
widgets:
|
||||||
- label:
|
- label:
|
||||||
text: "Button"
|
text: "Button"
|
||||||
|
- label:
|
||||||
|
id: hello_world_title_
|
||||||
|
text: ESPHome
|
||||||
|
text_font: montserrat_20
|
||||||
|
width: 100%
|
||||||
|
text_align: center
|
||||||
|
on_boot:
|
||||||
|
lvgl.widget.refresh: hello_world_title_
|
||||||
|
hidden: !lambda |-
|
||||||
|
return lv_obj_get_width(lv_scr_act()) < 400;
|
||||||
|
- checkbox:
|
||||||
|
text: Checkbox
|
||||||
|
id: hello_world_checkbox_
|
||||||
|
on_boot:
|
||||||
|
lvgl.widget.refresh: hello_world_checkbox_
|
||||||
|
hidden: !lambda |-
|
||||||
|
return lv_obj_get_width(lv_scr_act()) < 240;
|
||||||
|
on_click:
|
||||||
|
lvgl.label.update:
|
||||||
|
id: hello_world_label_
|
||||||
|
text: "Checked!"
|
||||||
|
- obj:
|
||||||
|
id: hello_world_container_
|
||||||
|
align: center
|
||||||
|
y: 14
|
||||||
|
pad_all: 0
|
||||||
|
outline_width: 0
|
||||||
|
border_width: 0
|
||||||
|
width: 100%
|
||||||
|
height: size_content
|
||||||
|
scrollable: false
|
||||||
|
on_click:
|
||||||
|
lvgl.spinner.update:
|
||||||
|
id: hello_world_spinner_
|
||||||
|
arc_color: springgreen
|
||||||
|
layout:
|
||||||
|
type: flex
|
||||||
|
flex_flow: row_wrap
|
||||||
|
flex_align_cross: center
|
||||||
|
flex_align_track: center
|
||||||
|
flex_align_main: space_evenly
|
||||||
|
widgets:
|
||||||
|
- spinner:
|
||||||
|
id: hello_world_spinner_
|
||||||
|
indicator:
|
||||||
|
arc_color: tomato
|
||||||
|
height: 100
|
||||||
|
width: 100
|
||||||
|
spin_time: 2s
|
||||||
|
arc_length: 60deg
|
||||||
|
widgets:
|
||||||
|
- label:
|
||||||
|
id: hello_world_label_
|
||||||
|
text: "Hello World!"
|
||||||
|
align: center
|
||||||
|
- obj:
|
||||||
|
id: hello_world_qrcode_
|
||||||
|
outline_width: 0
|
||||||
|
border_width: 0
|
||||||
|
hidden: !lambda |-
|
||||||
|
return lv_obj_get_width(lv_scr_act()) < 300 && lv_obj_get_height(lv_scr_act()) < 400;
|
||||||
|
widgets:
|
||||||
|
- label:
|
||||||
|
text_font: montserrat_14
|
||||||
|
text: esphome.io
|
||||||
|
align: top_mid
|
||||||
|
- qrcode:
|
||||||
|
text: "https://esphome.io"
|
||||||
|
size: 80
|
||||||
|
align: bottom_mid
|
||||||
|
on_boot:
|
||||||
|
lvgl.widget.refresh: hello_world_qrcode_
|
||||||
|
|
||||||
- slider:
|
- slider:
|
||||||
width: 80%
|
width: 80%
|
||||||
align: bottom_mid
|
align: bottom_mid
|
||||||
|
@@ -67,7 +67,6 @@ class Widget:
|
|||||||
self.type = wtype
|
self.type = wtype
|
||||||
self.config = config
|
self.config = config
|
||||||
self.scale = 1.0
|
self.scale = 1.0
|
||||||
self.step = 1.0
|
|
||||||
self.range_from = -sys.maxsize
|
self.range_from = -sys.maxsize
|
||||||
self.range_to = sys.maxsize
|
self.range_to = sys.maxsize
|
||||||
if wtype.is_compound():
|
if wtype.is_compound():
|
||||||
|
@@ -11,6 +11,7 @@ from ..defines import (
|
|||||||
CONF_ROLLOVER,
|
CONF_ROLLOVER,
|
||||||
CONF_SCROLLBAR,
|
CONF_SCROLLBAR,
|
||||||
CONF_SELECTED,
|
CONF_SELECTED,
|
||||||
|
CONF_SELECTED_DIGIT,
|
||||||
CONF_TEXTAREA_PLACEHOLDER,
|
CONF_TEXTAREA_PLACEHOLDER,
|
||||||
)
|
)
|
||||||
from ..lv_validation import lv_bool, lv_float
|
from ..lv_validation import lv_bool, lv_float
|
||||||
@@ -38,18 +39,24 @@ def validate_spinbox(config):
|
|||||||
min_val = -1 - max_val
|
min_val = -1 - max_val
|
||||||
range_from = int(config[CONF_RANGE_FROM])
|
range_from = int(config[CONF_RANGE_FROM])
|
||||||
range_to = int(config[CONF_RANGE_TO])
|
range_to = int(config[CONF_RANGE_TO])
|
||||||
step = int(config[CONF_STEP])
|
step = config[CONF_SELECTED_DIGIT]
|
||||||
|
digits = config[CONF_DIGITS]
|
||||||
if (
|
if (
|
||||||
range_from > max_val
|
range_from > max_val
|
||||||
or range_from < min_val
|
or range_from < min_val
|
||||||
or range_to > max_val
|
or range_to > max_val
|
||||||
or range_to < min_val
|
or range_to < min_val
|
||||||
):
|
):
|
||||||
raise cv.Invalid("Range outside allowed limits")
|
raise cv.Invalid("Range outside allowed limits", path=[CONF_RANGE_FROM])
|
||||||
if step <= 0 or step >= (range_to - range_from) / 2:
|
if digits <= config[CONF_DECIMAL_PLACES]:
|
||||||
raise cv.Invalid("Invalid step value")
|
raise cv.Invalid(
|
||||||
if config[CONF_DIGITS] <= config[CONF_DECIMAL_PLACES]:
|
"Number of digits must exceed number of decimal places", path=[CONF_DIGITS]
|
||||||
raise cv.Invalid("Number of digits must exceed number of decimal places")
|
)
|
||||||
|
if step >= digits:
|
||||||
|
raise cv.Invalid(
|
||||||
|
"Initial selected digit must be less than number of digits",
|
||||||
|
path=[CONF_SELECTED_DIGIT],
|
||||||
|
)
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
@@ -59,7 +66,10 @@ SPINBOX_SCHEMA = cv.Schema(
|
|||||||
cv.Optional(CONF_RANGE_FROM, default=0): cv.float_,
|
cv.Optional(CONF_RANGE_FROM, default=0): cv.float_,
|
||||||
cv.Optional(CONF_RANGE_TO, default=100): cv.float_,
|
cv.Optional(CONF_RANGE_TO, default=100): cv.float_,
|
||||||
cv.Optional(CONF_DIGITS, default=4): cv.int_range(1, 10),
|
cv.Optional(CONF_DIGITS, default=4): cv.int_range(1, 10),
|
||||||
cv.Optional(CONF_STEP, default=1.0): cv.positive_float,
|
cv.Optional(CONF_STEP): cv.invalid(
|
||||||
|
f"{CONF_STEP} has been replaced by {CONF_SELECTED_DIGIT}"
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_SELECTED_DIGIT, default=0): cv.positive_int,
|
||||||
cv.Optional(CONF_DECIMAL_PLACES, default=0): cv.int_range(0, 6),
|
cv.Optional(CONF_DECIMAL_PLACES, default=0): cv.int_range(0, 6),
|
||||||
cv.Optional(CONF_ROLLOVER, default=False): lv_bool,
|
cv.Optional(CONF_ROLLOVER, default=False): lv_bool,
|
||||||
}
|
}
|
||||||
@@ -93,13 +103,12 @@ class SpinboxType(WidgetType):
|
|||||||
scale = 10 ** config[CONF_DECIMAL_PLACES]
|
scale = 10 ** config[CONF_DECIMAL_PLACES]
|
||||||
range_from = int(config[CONF_RANGE_FROM]) * scale
|
range_from = int(config[CONF_RANGE_FROM]) * scale
|
||||||
range_to = int(config[CONF_RANGE_TO]) * scale
|
range_to = int(config[CONF_RANGE_TO]) * scale
|
||||||
step = int(config[CONF_STEP]) * scale
|
step = config[CONF_SELECTED_DIGIT]
|
||||||
w.scale = scale
|
w.scale = scale
|
||||||
w.step = step
|
|
||||||
w.range_to = range_to
|
w.range_to = range_to
|
||||||
w.range_from = range_from
|
w.range_from = range_from
|
||||||
lv.spinbox_set_range(w.obj, range_from, range_to)
|
lv.spinbox_set_range(w.obj, range_from, range_to)
|
||||||
await w.set_property(CONF_STEP, step)
|
await w.set_property("step", 10**step)
|
||||||
await w.set_property(CONF_ROLLOVER, config)
|
await w.set_property(CONF_ROLLOVER, config)
|
||||||
lv.spinbox_set_digit_format(
|
lv.spinbox_set_digit_format(
|
||||||
w.obj, digits, digits - config[CONF_DECIMAL_PLACES]
|
w.obj, digits, digits - config[CONF_DECIMAL_PLACES]
|
||||||
@@ -120,7 +129,7 @@ class SpinboxType(WidgetType):
|
|||||||
return config[CONF_RANGE_FROM]
|
return config[CONF_RANGE_FROM]
|
||||||
|
|
||||||
def get_step(self, config: dict):
|
def get_step(self, config: dict):
|
||||||
return config[CONF_STEP]
|
return 10 ** config[CONF_SELECTED_DIGIT]
|
||||||
|
|
||||||
|
|
||||||
spinbox_spec = SpinboxType()
|
spinbox_spec = SpinboxType()
|
||||||
|
@@ -10,7 +10,8 @@ from esphome.loader import get_component
|
|||||||
CODEOWNERS = ["@clydebarrow"]
|
CODEOWNERS = ["@clydebarrow"]
|
||||||
MULTI_CONF = True
|
MULTI_CONF = True
|
||||||
|
|
||||||
map_ = cg.std_ns.class_("map")
|
mapping_ns = cg.esphome_ns.namespace("mapping")
|
||||||
|
mapping_class = mapping_ns.class_("Mapping")
|
||||||
|
|
||||||
CONF_ENTRIES = "entries"
|
CONF_ENTRIES = "entries"
|
||||||
CONF_CLASS = "class"
|
CONF_CLASS = "class"
|
||||||
@@ -29,7 +30,11 @@ class IndexType:
|
|||||||
|
|
||||||
INDEX_TYPES = {
|
INDEX_TYPES = {
|
||||||
"int": IndexType(cv.int_, cg.int_, int),
|
"int": IndexType(cv.int_, cg.int_, int),
|
||||||
"string": IndexType(cv.string, cg.std_string, str),
|
"string": IndexType(
|
||||||
|
cv.string,
|
||||||
|
cg.std_string,
|
||||||
|
str,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -47,7 +52,7 @@ def to_schema(value):
|
|||||||
|
|
||||||
BASE_SCHEMA = cv.Schema(
|
BASE_SCHEMA = cv.Schema(
|
||||||
{
|
{
|
||||||
cv.Required(CONF_ID): cv.declare_id(map_),
|
cv.Required(CONF_ID): cv.declare_id(mapping_class),
|
||||||
cv.Required(CONF_FROM): cv.one_of(*INDEX_TYPES, lower=True),
|
cv.Required(CONF_FROM): cv.one_of(*INDEX_TYPES, lower=True),
|
||||||
cv.Required(CONF_TO): cv.string,
|
cv.Required(CONF_TO): cv.string,
|
||||||
},
|
},
|
||||||
@@ -123,12 +128,15 @@ async def to_code(config):
|
|||||||
if list(entries.values())[0].op != ".":
|
if list(entries.values())[0].op != ".":
|
||||||
value_type = value_type.operator("ptr")
|
value_type = value_type.operator("ptr")
|
||||||
varid = config[CONF_ID]
|
varid = config[CONF_ID]
|
||||||
varid.type = map_.template(index_type, value_type)
|
varid.type = mapping_class.template(
|
||||||
|
index_type,
|
||||||
|
value_type,
|
||||||
|
)
|
||||||
var = MockObj(varid, ".")
|
var = MockObj(varid, ".")
|
||||||
decl = VariableDeclarationExpression(varid.type, "", varid)
|
decl = VariableDeclarationExpression(varid.type, "", varid)
|
||||||
add_global(decl)
|
add_global(decl)
|
||||||
CORE.register_variable(varid, var)
|
CORE.register_variable(varid, var)
|
||||||
|
|
||||||
for key, value in entries.items():
|
for key, value in entries.items():
|
||||||
cg.add(var.insert((key, value)))
|
cg.add(var.set(key, value))
|
||||||
return var
|
return var
|
||||||
|
69
esphome/components/mapping/mapping.h
Normal file
69
esphome/components/mapping/mapping.h
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace esphome::mapping {
|
||||||
|
|
||||||
|
using alloc_string_t = std::basic_string<char, std::char_traits<char>, RAMAllocator<char>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Mapping class with custom allocator.
|
||||||
|
* Additionally, when std::string is used as key or value, it will be replaced with a custom string type
|
||||||
|
* that uses RAMAllocator.
|
||||||
|
* @tparam K The type of the key in the mapping.
|
||||||
|
* @tparam V The type of the value in the mapping. Should be a basic type or pointer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const char *const TAG = "mapping";
|
||||||
|
|
||||||
|
template<typename K, typename V> class Mapping {
|
||||||
|
public:
|
||||||
|
// Constructor
|
||||||
|
Mapping() = default;
|
||||||
|
|
||||||
|
using key_t = const std::conditional_t<std::is_same_v<K, std::string>,
|
||||||
|
alloc_string_t, // if K is std::string, custom string type
|
||||||
|
K>;
|
||||||
|
using value_t = std::conditional_t<std::is_same_v<V, std::string>,
|
||||||
|
alloc_string_t, // if V is std::string, custom string type
|
||||||
|
V>;
|
||||||
|
|
||||||
|
void set(const K &key, const V &value) { this->map_[key_t{key}] = value; }
|
||||||
|
|
||||||
|
V get(const K &key) const {
|
||||||
|
auto it = this->map_.find(key_t{key});
|
||||||
|
if (it != this->map_.end()) {
|
||||||
|
return V{it->second};
|
||||||
|
}
|
||||||
|
if constexpr (std::is_pointer_v<K>) {
|
||||||
|
esph_log_e(TAG, "Key '%p' not found in mapping", key);
|
||||||
|
} else if constexpr (std::is_same_v<K, std::string>) {
|
||||||
|
esph_log_e(TAG, "Key '%s' not found in mapping", key.c_str());
|
||||||
|
} else {
|
||||||
|
esph_log_e(TAG, "Key '%s' not found in mapping", to_string(key).c_str());
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// index map overload
|
||||||
|
V operator[](K key) { return this->get(key); }
|
||||||
|
|
||||||
|
// convenience function for strings to get a C-style string
|
||||||
|
template<typename T = V, std::enable_if_t<std::is_same_v<T, std::string>, int> = 0>
|
||||||
|
const char *operator[](K key) const {
|
||||||
|
auto it = this->map_.find(key_t{key});
|
||||||
|
if (it != this->map_.end()) {
|
||||||
|
return it->second.c_str(); // safe since value remains in map
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::map<key_t, value_t, std::less<key_t>, RAMAllocator<std::pair<key_t, value_t>>> map_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace esphome::mapping
|
@@ -198,7 +198,7 @@ uint16_t Mcp4461Component::get_wiper_level_(Mcp4461WiperIdx wiper) {
|
|||||||
|
|
||||||
uint16_t Mcp4461Component::read_wiper_level_(uint8_t wiper_idx) {
|
uint16_t Mcp4461Component::read_wiper_level_(uint8_t wiper_idx) {
|
||||||
uint8_t addr = this->get_wiper_address_(wiper_idx);
|
uint8_t addr = this->get_wiper_address_(wiper_idx);
|
||||||
uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::INCREMENT);
|
uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::READ);
|
||||||
if (wiper_idx > 3) {
|
if (wiper_idx > 3) {
|
||||||
if (!this->is_eeprom_ready_for_writing_(true)) {
|
if (!this->is_eeprom_ready_for_writing_(true)) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@@ -65,26 +65,47 @@ ACCELERATION_MODES = {
|
|||||||
"high": RhtAccelerationMode.HIGH_ACCELERATION,
|
"high": RhtAccelerationMode.HIGH_ACCELERATION,
|
||||||
}
|
}
|
||||||
|
|
||||||
GAS_SENSOR = cv.Schema(
|
|
||||||
|
def _gas_sensor(
|
||||||
|
*,
|
||||||
|
index_offset: int,
|
||||||
|
learning_time_offset: int,
|
||||||
|
learning_time_gain: int,
|
||||||
|
gating_max_duration: int,
|
||||||
|
std_initial: int,
|
||||||
|
gain_factor: int,
|
||||||
|
) -> cv.Schema:
|
||||||
|
return sensor.sensor_schema(
|
||||||
|
icon=ICON_RADIATOR,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_AQI,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
).extend(
|
||||||
{
|
{
|
||||||
cv.Optional(CONF_ALGORITHM_TUNING): cv.Schema(
|
cv.Optional(CONF_ALGORITHM_TUNING): cv.Schema(
|
||||||
{
|
{
|
||||||
cv.Optional(CONF_INDEX_OFFSET, default=100): cv.int_range(1, 250),
|
cv.Optional(CONF_INDEX_OFFSET, default=index_offset): cv.int_range(
|
||||||
cv.Optional(CONF_LEARNING_TIME_OFFSET_HOURS, default=12): cv.int_range(
|
1, 250
|
||||||
1, 1000
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_LEARNING_TIME_GAIN_HOURS, default=12): cv.int_range(
|
|
||||||
1, 1000
|
|
||||||
),
|
),
|
||||||
cv.Optional(
|
cv.Optional(
|
||||||
CONF_GATING_MAX_DURATION_MINUTES, default=720
|
CONF_LEARNING_TIME_OFFSET_HOURS, default=learning_time_offset
|
||||||
|
): cv.int_range(1, 1000),
|
||||||
|
cv.Optional(
|
||||||
|
CONF_LEARNING_TIME_GAIN_HOURS, default=learning_time_gain
|
||||||
|
): cv.int_range(1, 1000),
|
||||||
|
cv.Optional(
|
||||||
|
CONF_GATING_MAX_DURATION_MINUTES, default=gating_max_duration
|
||||||
): cv.int_range(0, 3000),
|
): cv.int_range(0, 3000),
|
||||||
cv.Optional(CONF_STD_INITIAL, default=50): cv.int_,
|
cv.Optional(CONF_STD_INITIAL, default=std_initial): cv.int_range(
|
||||||
cv.Optional(CONF_GAIN_FACTOR, default=230): cv.int_range(1, 1000),
|
10, 5000
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_GAIN_FACTOR, default=gain_factor): cv.int_range(
|
||||||
|
1, 1000
|
||||||
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def float_previously_pct(value):
|
def float_previously_pct(value):
|
||||||
@@ -127,18 +148,22 @@ CONFIG_SCHEMA = (
|
|||||||
state_class=STATE_CLASS_MEASUREMENT,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_AUTO_CLEANING_INTERVAL): cv.update_interval,
|
cv.Optional(CONF_AUTO_CLEANING_INTERVAL): cv.update_interval,
|
||||||
cv.Optional(CONF_VOC): sensor.sensor_schema(
|
cv.Optional(CONF_VOC): _gas_sensor(
|
||||||
icon=ICON_RADIATOR,
|
index_offset=100,
|
||||||
accuracy_decimals=0,
|
learning_time_offset=12,
|
||||||
device_class=DEVICE_CLASS_AQI,
|
learning_time_gain=12,
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
gating_max_duration=180,
|
||||||
).extend(GAS_SENSOR),
|
std_initial=50,
|
||||||
cv.Optional(CONF_NOX): sensor.sensor_schema(
|
gain_factor=230,
|
||||||
icon=ICON_RADIATOR,
|
),
|
||||||
accuracy_decimals=0,
|
cv.Optional(CONF_NOX): _gas_sensor(
|
||||||
device_class=DEVICE_CLASS_AQI,
|
index_offset=1,
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
learning_time_offset=12,
|
||||||
).extend(GAS_SENSOR),
|
learning_time_gain=12,
|
||||||
|
gating_max_duration=720,
|
||||||
|
std_initial=50,
|
||||||
|
gain_factor=230,
|
||||||
|
),
|
||||||
cv.Optional(CONF_STORE_BASELINE, default=True): cv.boolean,
|
cv.Optional(CONF_STORE_BASELINE, default=True): cv.boolean,
|
||||||
cv.Optional(CONF_VOC_BASELINE): cv.hex_uint16_t,
|
cv.Optional(CONF_VOC_BASELINE): cv.hex_uint16_t,
|
||||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||||
@@ -194,16 +219,15 @@ async def to_code(config):
|
|||||||
await i2c.register_i2c_device(var, config)
|
await i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
for key, funcName in SETTING_MAP.items():
|
for key, funcName in SETTING_MAP.items():
|
||||||
if key in config:
|
if cfg := config.get(key):
|
||||||
cg.add(getattr(var, funcName)(config[key]))
|
cg.add(getattr(var, funcName)(cfg))
|
||||||
|
|
||||||
for key, funcName in SENSOR_MAP.items():
|
for key, funcName in SENSOR_MAP.items():
|
||||||
if key in config:
|
if cfg := config.get(key):
|
||||||
sens = await sensor.new_sensor(config[key])
|
sens = await sensor.new_sensor(cfg)
|
||||||
cg.add(getattr(var, funcName)(sens))
|
cg.add(getattr(var, funcName)(sens))
|
||||||
|
|
||||||
if CONF_VOC in config and CONF_ALGORITHM_TUNING in config[CONF_VOC]:
|
if cfg := config.get(CONF_VOC, {}).get(CONF_ALGORITHM_TUNING):
|
||||||
cfg = config[CONF_VOC][CONF_ALGORITHM_TUNING]
|
|
||||||
cg.add(
|
cg.add(
|
||||||
var.set_voc_algorithm_tuning(
|
var.set_voc_algorithm_tuning(
|
||||||
cfg[CONF_INDEX_OFFSET],
|
cfg[CONF_INDEX_OFFSET],
|
||||||
@@ -214,8 +238,7 @@ async def to_code(config):
|
|||||||
cfg[CONF_GAIN_FACTOR],
|
cfg[CONF_GAIN_FACTOR],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if CONF_NOX in config and CONF_ALGORITHM_TUNING in config[CONF_NOX]:
|
if cfg := config.get(CONF_NOX, {}).get(CONF_ALGORITHM_TUNING):
|
||||||
cfg = config[CONF_NOX][CONF_ALGORITHM_TUNING]
|
|
||||||
cg.add(
|
cg.add(
|
||||||
var.set_nox_algorithm_tuning(
|
var.set_nox_algorithm_tuning(
|
||||||
cfg[CONF_INDEX_OFFSET],
|
cfg[CONF_INDEX_OFFSET],
|
||||||
@@ -225,12 +248,12 @@ async def to_code(config):
|
|||||||
cfg[CONF_GAIN_FACTOR],
|
cfg[CONF_GAIN_FACTOR],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if CONF_TEMPERATURE_COMPENSATION in config:
|
if cfg := config.get(CONF_TEMPERATURE_COMPENSATION):
|
||||||
cg.add(
|
cg.add(
|
||||||
var.set_temperature_compensation(
|
var.set_temperature_compensation(
|
||||||
config[CONF_TEMPERATURE_COMPENSATION][CONF_OFFSET],
|
cfg[CONF_OFFSET],
|
||||||
config[CONF_TEMPERATURE_COMPENSATION][CONF_NORMALIZED_OFFSET_SLOPE],
|
cfg[CONF_NORMALIZED_OFFSET_SLOPE],
|
||||||
config[CONF_TEMPERATURE_COMPENSATION][CONF_TIME_CONSTANT],
|
cfg[CONF_TIME_CONSTANT],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -11,7 +11,7 @@ pyserial==3.5
|
|||||||
platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile
|
platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile
|
||||||
esptool==5.0.2
|
esptool==5.0.2
|
||||||
click==8.1.7
|
click==8.1.7
|
||||||
esphome-dashboard==20250814.0
|
esphome-dashboard==20250828.0
|
||||||
aioesphomeapi==39.0.0
|
aioesphomeapi==39.0.0
|
||||||
zeroconf==0.147.0
|
zeroconf==0.147.0
|
||||||
puremagic==1.30
|
puremagic==1.30
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
pylint==3.3.8
|
pylint==3.3.8
|
||||||
flake8==7.3.0 # also change in .pre-commit-config.yaml when updating
|
flake8==7.3.0 # also change in .pre-commit-config.yaml when updating
|
||||||
ruff==0.12.10 # also change in .pre-commit-config.yaml when updating
|
ruff==0.12.11 # also change in .pre-commit-config.yaml when updating
|
||||||
pyupgrade==3.20.0 # also change in .pre-commit-config.yaml when updating
|
pyupgrade==3.20.0 # also change in .pre-commit-config.yaml when updating
|
||||||
pre-commit
|
pre-commit
|
||||||
|
|
||||||
|
@@ -684,7 +684,7 @@ lvgl:
|
|||||||
width: 120
|
width: 120
|
||||||
range_from: -10
|
range_from: -10
|
||||||
range_to: 1000
|
range_to: 1000
|
||||||
step: 5.0
|
selected_digit: 2
|
||||||
rollover: false
|
rollover: false
|
||||||
digits: 6
|
digits: 6
|
||||||
decimal_places: 2
|
decimal_places: 2
|
||||||
|
1
tests/components/mapping/.gitattributes
vendored
Normal file
1
tests/components/mapping/.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.ttf -text
|
@@ -50,6 +50,14 @@ mapping:
|
|||||||
red: red_id
|
red: red_id
|
||||||
blue: blue_id
|
blue: blue_id
|
||||||
green: green_id
|
green: green_id
|
||||||
|
- id: string_map_2
|
||||||
|
from: string
|
||||||
|
to: string
|
||||||
|
entries:
|
||||||
|
one: "one"
|
||||||
|
two: "two"
|
||||||
|
three: "three"
|
||||||
|
seventy-seven: "seventy-seven"
|
||||||
|
|
||||||
color:
|
color:
|
||||||
- id: red_id
|
- id: red_id
|
||||||
@@ -65,7 +73,14 @@ color:
|
|||||||
green: 0.0
|
green: 0.0
|
||||||
blue: 1.0
|
blue: 1.0
|
||||||
|
|
||||||
|
font:
|
||||||
|
- file: "$component_dir/helvetica.ttf"
|
||||||
|
id: font_id
|
||||||
|
size: 20
|
||||||
|
|
||||||
display:
|
display:
|
||||||
lambda: |-
|
lambda: |-
|
||||||
it.image(0, 0, id(weather_map)[0]);
|
std::string value = id(int_map)[2];
|
||||||
it.image(0, 100, id(weather_map)[1]);
|
it.print(0, 0, id(font_id), TextAlign::TOP_LEFT, value.c_str());
|
||||||
|
it.image(0, 0, id(weather_map)["clear-night"]);
|
||||||
|
it.image(0, 100, id(weather_map)["sunny"]);
|
||||||
|
BIN
tests/components/mapping/helvetica.ttf
Normal file
BIN
tests/components/mapping/helvetica.ttf
Normal file
Binary file not shown.
@@ -4,14 +4,14 @@ spi:
|
|||||||
mosi_pin: 17
|
mosi_pin: 17
|
||||||
miso_pin: 15
|
miso_pin: 15
|
||||||
|
|
||||||
|
packages:
|
||||||
|
map: !include common.yaml
|
||||||
|
|
||||||
display:
|
display:
|
||||||
- platform: ili9xxx
|
platform: ili9xxx
|
||||||
id: main_lcd
|
id: main_lcd
|
||||||
model: ili9342
|
model: ili9342
|
||||||
cs_pin: 12
|
cs_pin: 12
|
||||||
dc_pin: 13
|
dc_pin: 13
|
||||||
reset_pin: 21
|
reset_pin: 21
|
||||||
invert_colors: false
|
invert_colors: false
|
||||||
|
|
||||||
packages:
|
|
||||||
map: !include common.yaml
|
|
||||||
|
@@ -5,7 +5,7 @@ spi:
|
|||||||
miso_pin: 5
|
miso_pin: 5
|
||||||
|
|
||||||
display:
|
display:
|
||||||
- platform: ili9xxx
|
platform: ili9xxx
|
||||||
id: main_lcd
|
id: main_lcd
|
||||||
model: ili9342
|
model: ili9342
|
||||||
cs_pin: 8
|
cs_pin: 8
|
||||||
|
@@ -5,7 +5,7 @@ spi:
|
|||||||
miso_pin: 5
|
miso_pin: 5
|
||||||
|
|
||||||
display:
|
display:
|
||||||
- platform: ili9xxx
|
platform: ili9xxx
|
||||||
id: main_lcd
|
id: main_lcd
|
||||||
model: ili9342
|
model: ili9342
|
||||||
cs_pin: 8
|
cs_pin: 8
|
||||||
|
@@ -5,7 +5,7 @@ spi:
|
|||||||
miso_pin: 15
|
miso_pin: 15
|
||||||
|
|
||||||
display:
|
display:
|
||||||
- platform: ili9xxx
|
platform: ili9xxx
|
||||||
id: main_lcd
|
id: main_lcd
|
||||||
model: ili9342
|
model: ili9342
|
||||||
cs_pin: 12
|
cs_pin: 12
|
||||||
|
@@ -5,7 +5,7 @@ spi:
|
|||||||
miso_pin: 12
|
miso_pin: 12
|
||||||
|
|
||||||
display:
|
display:
|
||||||
- platform: ili9xxx
|
platform: ili9xxx
|
||||||
id: main_lcd
|
id: main_lcd
|
||||||
model: ili9342
|
model: ili9342
|
||||||
cs_pin: 5
|
cs_pin: 5
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
display:
|
display:
|
||||||
- platform: sdl
|
platform: sdl
|
||||||
id: sdl_display
|
id: sdl_display
|
||||||
update_interval: 1s
|
update_interval: 1s
|
||||||
auto_clear_enabled: false
|
auto_clear_enabled: false
|
||||||
|
@@ -5,7 +5,7 @@ spi:
|
|||||||
miso_pin: 4
|
miso_pin: 4
|
||||||
|
|
||||||
display:
|
display:
|
||||||
- platform: ili9xxx
|
platform: ili9xxx
|
||||||
id: main_lcd
|
id: main_lcd
|
||||||
model: ili9342
|
model: ili9342
|
||||||
cs_pin: 20
|
cs_pin: 20
|
||||||
|
Reference in New Issue
Block a user