esphome 2025.4.0__py3-none-any.whl → 2025.4.0b2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- esphome/__main__.py +3 -5
- esphome/components/am2315c/am2315c.cpp +4 -4
- esphome/components/sml/sml.cpp +6 -8
- esphome/components/sml/sml.h +1 -1
- esphome/components/sml/sml_parser.cpp +18 -16
- esphome/components/sml/sml_parser.h +12 -42
- esphome/config_validation.py +42 -0
- esphome/const.py +1 -1
- esphome/core/__init__.py +1 -0
- esphome/vscode.py +8 -27
- esphome/yaml_util.py +39 -75
- {esphome-2025.4.0.dist-info → esphome-2025.4.0b2.dist-info}/METADATA +2 -2
- {esphome-2025.4.0.dist-info → esphome-2025.4.0b2.dist-info}/RECORD +17 -17
- {esphome-2025.4.0.dist-info → esphome-2025.4.0b2.dist-info}/WHEEL +0 -0
- {esphome-2025.4.0.dist-info → esphome-2025.4.0b2.dist-info}/entry_points.txt +0 -0
- {esphome-2025.4.0.dist-info → esphome-2025.4.0b2.dist-info}/licenses/LICENSE +0 -0
- {esphome-2025.4.0.dist-info → esphome-2025.4.0b2.dist-info}/top_level.txt +0 -0
esphome/__main__.py
CHANGED
@@ -375,12 +375,10 @@ def upload_program(config, args, host):
|
|
375
375
|
password = ota_conf.get(CONF_PASSWORD, "")
|
376
376
|
|
377
377
|
if (
|
378
|
-
|
378
|
+
not is_ip_address(CORE.address) # pylint: disable=too-many-boolean-expressions
|
379
|
+
and (get_port_type(host) == "MQTT" or config[CONF_MDNS][CONF_DISABLED])
|
380
|
+
and CONF_MQTT in config
|
379
381
|
and (not args.device or args.device in ("MQTT", "OTA"))
|
380
|
-
and (
|
381
|
-
((config[CONF_MDNS][CONF_DISABLED]) and not is_ip_address(CORE.address))
|
382
|
-
or get_port_type(host) == "MQTT"
|
383
|
-
)
|
384
382
|
):
|
385
383
|
from esphome import mqtt
|
386
384
|
|
@@ -128,7 +128,7 @@ void AM2315C::update() {
|
|
128
128
|
data[2] = 0x00;
|
129
129
|
if (this->write(data, 3) != i2c::ERROR_OK) {
|
130
130
|
ESP_LOGE(TAG, "Write failed!");
|
131
|
-
this->
|
131
|
+
this->mark_failed();
|
132
132
|
return;
|
133
133
|
}
|
134
134
|
|
@@ -138,12 +138,12 @@ void AM2315C::update() {
|
|
138
138
|
uint8_t status = 0;
|
139
139
|
if (this->read(&status, 1) != i2c::ERROR_OK) {
|
140
140
|
ESP_LOGE(TAG, "Read failed!");
|
141
|
-
this->
|
141
|
+
this->mark_failed();
|
142
142
|
return;
|
143
143
|
}
|
144
144
|
if ((status & 0x80) == 0x80) {
|
145
145
|
ESP_LOGE(TAG, "HW still busy!");
|
146
|
-
this->
|
146
|
+
this->mark_failed();
|
147
147
|
return;
|
148
148
|
}
|
149
149
|
|
@@ -151,7 +151,7 @@ void AM2315C::update() {
|
|
151
151
|
uint8_t data[7];
|
152
152
|
if (this->read(data, 7) != i2c::ERROR_OK) {
|
153
153
|
ESP_LOGE(TAG, "Read failed!");
|
154
|
-
this->
|
154
|
+
this->mark_failed();
|
155
155
|
return;
|
156
156
|
}
|
157
157
|
|
esphome/components/sml/sml.cpp
CHANGED
@@ -52,8 +52,9 @@ void Sml::loop() {
|
|
52
52
|
break;
|
53
53
|
|
54
54
|
// remove start/end sequence
|
55
|
-
this->
|
56
|
-
|
55
|
+
this->sml_data_.erase(this->sml_data_.begin(), this->sml_data_.begin() + START_SEQ.size());
|
56
|
+
this->sml_data_.resize(this->sml_data_.size() - 8);
|
57
|
+
this->process_sml_file_(this->sml_data_);
|
57
58
|
}
|
58
59
|
break;
|
59
60
|
};
|
@@ -65,8 +66,8 @@ void Sml::add_on_data_callback(std::function<void(std::vector<uint8_t>, bool)> &
|
|
65
66
|
this->data_callbacks_.add(std::move(callback));
|
66
67
|
}
|
67
68
|
|
68
|
-
void Sml::process_sml_file_(const
|
69
|
-
SmlFile sml_file(sml_data);
|
69
|
+
void Sml::process_sml_file_(const bytes &sml_data) {
|
70
|
+
SmlFile sml_file = SmlFile(sml_data);
|
70
71
|
std::vector<ObisInfo> obis_info = sml_file.get_obis_info();
|
71
72
|
this->publish_obis_info_(obis_info);
|
72
73
|
|
@@ -74,7 +75,6 @@ void Sml::process_sml_file_(const BytesView &sml_data) {
|
|
74
75
|
}
|
75
76
|
|
76
77
|
void Sml::log_obis_info_(const std::vector<ObisInfo> &obis_info_vec) {
|
77
|
-
#ifdef ESPHOME_LOG_HAS_DEBUG
|
78
78
|
ESP_LOGD(TAG, "OBIS info:");
|
79
79
|
for (auto const &obis_info : obis_info_vec) {
|
80
80
|
std::string info;
|
@@ -83,7 +83,6 @@ void Sml::log_obis_info_(const std::vector<ObisInfo> &obis_info_vec) {
|
|
83
83
|
info += " [0x" + bytes_repr(obis_info.value) + "]";
|
84
84
|
ESP_LOGD(TAG, "%s", info.c_str());
|
85
85
|
}
|
86
|
-
#endif
|
87
86
|
}
|
88
87
|
|
89
88
|
void Sml::publish_obis_info_(const std::vector<ObisInfo> &obis_info_vec) {
|
@@ -93,11 +92,10 @@ void Sml::publish_obis_info_(const std::vector<ObisInfo> &obis_info_vec) {
|
|
93
92
|
}
|
94
93
|
|
95
94
|
void Sml::publish_value_(const ObisInfo &obis_info) {
|
96
|
-
const auto obis_code = obis_info.code_repr();
|
97
95
|
for (auto const &sml_listener : sml_listeners_) {
|
98
96
|
if ((!sml_listener->server_id.empty()) && (bytes_repr(obis_info.server_id) != sml_listener->server_id))
|
99
97
|
continue;
|
100
|
-
if (
|
98
|
+
if (obis_info.code_repr() != sml_listener->obis_code)
|
101
99
|
continue;
|
102
100
|
sml_listener->publish_val(obis_info);
|
103
101
|
}
|
esphome/components/sml/sml.h
CHANGED
@@ -27,7 +27,7 @@ class Sml : public Component, public uart::UARTDevice {
|
|
27
27
|
void add_on_data_callback(std::function<void(std::vector<uint8_t>, bool)> &&callback);
|
28
28
|
|
29
29
|
protected:
|
30
|
-
void process_sml_file_(const
|
30
|
+
void process_sml_file_(const bytes &sml_data);
|
31
31
|
void log_obis_info_(const std::vector<ObisInfo> &obis_info_vec);
|
32
32
|
void publish_obis_info_(const std::vector<ObisInfo> &obis_info_vec);
|
33
33
|
char check_start_end_bytes_(uint8_t byte);
|
@@ -5,17 +5,17 @@
|
|
5
5
|
namespace esphome {
|
6
6
|
namespace sml {
|
7
7
|
|
8
|
-
SmlFile::SmlFile(
|
8
|
+
SmlFile::SmlFile(bytes buffer) : buffer_(std::move(buffer)) {
|
9
9
|
// extract messages
|
10
10
|
this->pos_ = 0;
|
11
11
|
while (this->pos_ < this->buffer_.size()) {
|
12
12
|
if (this->buffer_[this->pos_] == 0x00)
|
13
13
|
break; // EndOfSmlMsg
|
14
14
|
|
15
|
-
SmlNode message;
|
15
|
+
SmlNode message = SmlNode();
|
16
16
|
if (!this->setup_node(&message))
|
17
17
|
break;
|
18
|
-
this->messages.emplace_back(
|
18
|
+
this->messages.emplace_back(message);
|
19
19
|
}
|
20
20
|
}
|
21
21
|
|
@@ -62,20 +62,22 @@ bool SmlFile::setup_node(SmlNode *node) {
|
|
62
62
|
return false;
|
63
63
|
|
64
64
|
node->type = type;
|
65
|
+
node->nodes.clear();
|
66
|
+
node->value_bytes.clear();
|
65
67
|
|
66
68
|
if (type == SML_LIST) {
|
67
69
|
node->nodes.reserve(length);
|
68
70
|
for (size_t i = 0; i != length; i++) {
|
69
|
-
SmlNode child_node;
|
71
|
+
SmlNode child_node = SmlNode();
|
70
72
|
if (!this->setup_node(&child_node))
|
71
73
|
return false;
|
72
|
-
node->nodes.emplace_back(
|
74
|
+
node->nodes.emplace_back(child_node);
|
73
75
|
}
|
74
76
|
} else {
|
75
77
|
// Value starts at the current position
|
76
78
|
// Value ends "length" bytes later,
|
77
79
|
// (since the TL field is counted but already subtracted from length)
|
78
|
-
node->value_bytes = buffer_.
|
80
|
+
node->value_bytes = bytes(this->buffer_.begin() + this->pos_, this->buffer_.begin() + this->pos_ + length);
|
79
81
|
// Increment the pointer past all consumed bytes
|
80
82
|
this->pos_ += length;
|
81
83
|
}
|
@@ -85,14 +87,14 @@ bool SmlFile::setup_node(SmlNode *node) {
|
|
85
87
|
std::vector<ObisInfo> SmlFile::get_obis_info() {
|
86
88
|
std::vector<ObisInfo> obis_info;
|
87
89
|
for (auto const &message : messages) {
|
88
|
-
|
90
|
+
SmlNode message_body = message.nodes[3];
|
89
91
|
uint16_t message_type = bytes_to_uint(message_body.nodes[0].value_bytes);
|
90
92
|
if (message_type != SML_GET_LIST_RES)
|
91
93
|
continue;
|
92
94
|
|
93
|
-
|
94
|
-
|
95
|
-
|
95
|
+
SmlNode get_list_response = message_body.nodes[1];
|
96
|
+
bytes server_id = get_list_response.nodes[1].value_bytes;
|
97
|
+
SmlNode val_list = get_list_response.nodes[4];
|
96
98
|
|
97
99
|
for (auto const &val_list_entry : val_list.nodes) {
|
98
100
|
obis_info.emplace_back(server_id, val_list_entry);
|
@@ -101,7 +103,7 @@ std::vector<ObisInfo> SmlFile::get_obis_info() {
|
|
101
103
|
return obis_info;
|
102
104
|
}
|
103
105
|
|
104
|
-
std::string bytes_repr(const
|
106
|
+
std::string bytes_repr(const bytes &buffer) {
|
105
107
|
std::string repr;
|
106
108
|
for (auto const value : buffer) {
|
107
109
|
repr += str_sprintf("%02x", value & 0xff);
|
@@ -109,7 +111,7 @@ std::string bytes_repr(const BytesView &buffer) {
|
|
109
111
|
return repr;
|
110
112
|
}
|
111
113
|
|
112
|
-
uint64_t bytes_to_uint(const
|
114
|
+
uint64_t bytes_to_uint(const bytes &buffer) {
|
113
115
|
uint64_t val = 0;
|
114
116
|
for (auto const value : buffer) {
|
115
117
|
val = (val << 8) + value;
|
@@ -117,7 +119,7 @@ uint64_t bytes_to_uint(const BytesView &buffer) {
|
|
117
119
|
return val;
|
118
120
|
}
|
119
121
|
|
120
|
-
int64_t bytes_to_int(const
|
122
|
+
int64_t bytes_to_int(const bytes &buffer) {
|
121
123
|
uint64_t tmp = bytes_to_uint(buffer);
|
122
124
|
int64_t val;
|
123
125
|
|
@@ -133,14 +135,14 @@ int64_t bytes_to_int(const BytesView &buffer) {
|
|
133
135
|
return val;
|
134
136
|
}
|
135
137
|
|
136
|
-
std::string bytes_to_string(const
|
138
|
+
std::string bytes_to_string(const bytes &buffer) { return std::string(buffer.begin(), buffer.end()); }
|
137
139
|
|
138
|
-
ObisInfo::ObisInfo(
|
140
|
+
ObisInfo::ObisInfo(bytes server_id, SmlNode val_list_entry) : server_id(std::move(server_id)) {
|
139
141
|
this->code = val_list_entry.nodes[0].value_bytes;
|
140
142
|
this->status = val_list_entry.nodes[1].value_bytes;
|
141
143
|
this->unit = bytes_to_uint(val_list_entry.nodes[3].value_bytes);
|
142
144
|
this->scaler = bytes_to_int(val_list_entry.nodes[4].value_bytes);
|
143
|
-
|
145
|
+
SmlNode value_node = val_list_entry.nodes[5];
|
144
146
|
this->value = value_node.value_bytes;
|
145
147
|
this->value_type = value_node.type;
|
146
148
|
}
|
@@ -1,6 +1,5 @@
|
|
1
1
|
#pragma once
|
2
2
|
|
3
|
-
#include <cassert>
|
4
3
|
#include <cstdint>
|
5
4
|
#include <cstdio>
|
6
5
|
#include <string>
|
@@ -12,73 +11,44 @@ namespace sml {
|
|
12
11
|
|
13
12
|
using bytes = std::vector<uint8_t>;
|
14
13
|
|
15
|
-
class BytesView {
|
16
|
-
public:
|
17
|
-
BytesView() noexcept = default;
|
18
|
-
|
19
|
-
explicit BytesView(const uint8_t *first, size_t count) noexcept : data_{first}, count_{count} {}
|
20
|
-
|
21
|
-
explicit BytesView(const bytes &bytes) noexcept : data_{bytes.data()}, count_{bytes.size()} {}
|
22
|
-
|
23
|
-
size_t size() const noexcept { return count_; }
|
24
|
-
|
25
|
-
uint8_t operator[](size_t index) const noexcept {
|
26
|
-
assert(index < count_);
|
27
|
-
return data_[index];
|
28
|
-
}
|
29
|
-
|
30
|
-
BytesView subview(size_t offset, size_t count) const noexcept {
|
31
|
-
assert(offset + count <= count_);
|
32
|
-
return BytesView{data_ + offset, count};
|
33
|
-
}
|
34
|
-
|
35
|
-
const uint8_t *begin() const noexcept { return data_; }
|
36
|
-
|
37
|
-
const uint8_t *end() const noexcept { return data_ + count_; }
|
38
|
-
|
39
|
-
private:
|
40
|
-
const uint8_t *data_ = nullptr;
|
41
|
-
size_t count_ = 0;
|
42
|
-
};
|
43
|
-
|
44
14
|
class SmlNode {
|
45
15
|
public:
|
46
16
|
uint8_t type;
|
47
|
-
|
17
|
+
bytes value_bytes;
|
48
18
|
std::vector<SmlNode> nodes;
|
49
19
|
};
|
50
20
|
|
51
21
|
class ObisInfo {
|
52
22
|
public:
|
53
|
-
ObisInfo(
|
54
|
-
|
55
|
-
|
56
|
-
|
23
|
+
ObisInfo(bytes server_id, SmlNode val_list_entry);
|
24
|
+
bytes server_id;
|
25
|
+
bytes code;
|
26
|
+
bytes status;
|
57
27
|
char unit;
|
58
28
|
char scaler;
|
59
|
-
|
29
|
+
bytes value;
|
60
30
|
uint16_t value_type;
|
61
31
|
std::string code_repr() const;
|
62
32
|
};
|
63
33
|
|
64
34
|
class SmlFile {
|
65
35
|
public:
|
66
|
-
SmlFile(
|
36
|
+
SmlFile(bytes buffer);
|
67
37
|
bool setup_node(SmlNode *node);
|
68
38
|
std::vector<SmlNode> messages;
|
69
39
|
std::vector<ObisInfo> get_obis_info();
|
70
40
|
|
71
41
|
protected:
|
72
|
-
const
|
42
|
+
const bytes buffer_;
|
73
43
|
size_t pos_;
|
74
44
|
};
|
75
45
|
|
76
|
-
std::string bytes_repr(const
|
46
|
+
std::string bytes_repr(const bytes &buffer);
|
77
47
|
|
78
|
-
uint64_t bytes_to_uint(const
|
48
|
+
uint64_t bytes_to_uint(const bytes &buffer);
|
79
49
|
|
80
|
-
int64_t bytes_to_int(const
|
50
|
+
int64_t bytes_to_int(const bytes &buffer);
|
81
51
|
|
82
|
-
std::string bytes_to_string(const
|
52
|
+
std::string bytes_to_string(const bytes &buffer);
|
83
53
|
} // namespace sml
|
84
54
|
} // namespace esphome
|
esphome/config_validation.py
CHANGED
@@ -1499,9 +1499,30 @@ def dimensions(value):
|
|
1499
1499
|
|
1500
1500
|
|
1501
1501
|
def directory(value):
|
1502
|
+
import json
|
1503
|
+
|
1502
1504
|
value = string(value)
|
1503
1505
|
path = CORE.relative_config_path(value)
|
1504
1506
|
|
1507
|
+
if CORE.vscode and (
|
1508
|
+
not CORE.ace or os.path.abspath(path) == os.path.abspath(CORE.config_path)
|
1509
|
+
):
|
1510
|
+
print(
|
1511
|
+
json.dumps(
|
1512
|
+
{
|
1513
|
+
"type": "check_directory_exists",
|
1514
|
+
"path": path,
|
1515
|
+
}
|
1516
|
+
)
|
1517
|
+
)
|
1518
|
+
data = json.loads(input())
|
1519
|
+
assert data["type"] == "directory_exists_response"
|
1520
|
+
if data["content"]:
|
1521
|
+
return value
|
1522
|
+
raise Invalid(
|
1523
|
+
f"Could not find directory '{path}'. Please make sure it exists (full path: {os.path.abspath(path)})."
|
1524
|
+
)
|
1525
|
+
|
1505
1526
|
if not os.path.exists(path):
|
1506
1527
|
raise Invalid(
|
1507
1528
|
f"Could not find directory '{path}'. Please make sure it exists (full path: {os.path.abspath(path)})."
|
@@ -1514,9 +1535,30 @@ def directory(value):
|
|
1514
1535
|
|
1515
1536
|
|
1516
1537
|
def file_(value):
|
1538
|
+
import json
|
1539
|
+
|
1517
1540
|
value = string(value)
|
1518
1541
|
path = CORE.relative_config_path(value)
|
1519
1542
|
|
1543
|
+
if CORE.vscode and (
|
1544
|
+
not CORE.ace or os.path.abspath(path) == os.path.abspath(CORE.config_path)
|
1545
|
+
):
|
1546
|
+
print(
|
1547
|
+
json.dumps(
|
1548
|
+
{
|
1549
|
+
"type": "check_file_exists",
|
1550
|
+
"path": path,
|
1551
|
+
}
|
1552
|
+
)
|
1553
|
+
)
|
1554
|
+
data = json.loads(input())
|
1555
|
+
assert data["type"] == "file_exists_response"
|
1556
|
+
if data["content"]:
|
1557
|
+
return value
|
1558
|
+
raise Invalid(
|
1559
|
+
f"Could not find file '{path}'. Please make sure it exists (full path: {os.path.abspath(path)})."
|
1560
|
+
)
|
1561
|
+
|
1520
1562
|
if not os.path.exists(path):
|
1521
1563
|
raise Invalid(
|
1522
1564
|
f"Could not find file '{path}'. Please make sure it exists (full path: {os.path.abspath(path)})."
|
esphome/const.py
CHANGED
esphome/core/__init__.py
CHANGED
esphome/vscode.py
CHANGED
@@ -78,47 +78,28 @@ def _print_file_read_event(path: str) -> None:
|
|
78
78
|
)
|
79
79
|
|
80
80
|
|
81
|
-
def _request_and_get_stream_on_stdin(fname: str) -> StringIO:
|
82
|
-
_print_file_read_event(fname)
|
83
|
-
raw_yaml_stream = StringIO(_read_file_content_from_json_on_stdin())
|
84
|
-
return raw_yaml_stream
|
85
|
-
|
86
|
-
|
87
|
-
def _vscode_loader(fname: str) -> dict[str, Any]:
|
88
|
-
raw_yaml_stream = _request_and_get_stream_on_stdin(fname)
|
89
|
-
# it is required to set the name on StringIO so document on start_mark
|
90
|
-
# is set properly. Otherwise it is initialized with "<file>"
|
91
|
-
raw_yaml_stream.name = fname
|
92
|
-
return parse_yaml(fname, raw_yaml_stream, _vscode_loader)
|
93
|
-
|
94
|
-
|
95
|
-
def _ace_loader(fname: str) -> dict[str, Any]:
|
96
|
-
raw_yaml_stream = _request_and_get_stream_on_stdin(fname)
|
97
|
-
return parse_yaml(fname, raw_yaml_stream)
|
98
|
-
|
99
|
-
|
100
81
|
def read_config(args):
|
101
82
|
while True:
|
102
83
|
CORE.reset()
|
103
84
|
data = json.loads(input())
|
104
|
-
assert data["type"] == "validate"
|
105
|
-
if data["type"] == "exit":
|
106
|
-
return
|
85
|
+
assert data["type"] == "validate"
|
107
86
|
CORE.vscode = True
|
108
|
-
|
109
|
-
|
110
|
-
|
87
|
+
CORE.ace = args.ace
|
88
|
+
f = data["file"]
|
89
|
+
if CORE.ace:
|
90
|
+
CORE.config_path = os.path.join(args.configuration, f)
|
111
91
|
else:
|
112
92
|
CORE.config_path = data["file"]
|
113
|
-
loader = _vscode_loader
|
114
93
|
|
115
94
|
file_name = CORE.config_path
|
95
|
+
_print_file_read_event(file_name)
|
96
|
+
raw_yaml = _read_file_content_from_json_on_stdin()
|
116
97
|
command_line_substitutions: dict[str, Any] = (
|
117
98
|
dict(args.substitution) if args.substitution else {}
|
118
99
|
)
|
119
100
|
vs = VSCodeResult()
|
120
101
|
try:
|
121
|
-
config =
|
102
|
+
config = parse_yaml(file_name, StringIO(raw_yaml))
|
122
103
|
res = validate_config(config, command_line_substitutions)
|
123
104
|
except Exception as err: # pylint: disable=broad-except
|
124
105
|
vs.add_yaml_error(str(err))
|
esphome/yaml_util.py
CHANGED
@@ -3,12 +3,12 @@ from __future__ import annotations
|
|
3
3
|
import fnmatch
|
4
4
|
import functools
|
5
5
|
import inspect
|
6
|
-
from io import
|
6
|
+
from io import TextIOWrapper
|
7
7
|
from ipaddress import _BaseAddress
|
8
8
|
import logging
|
9
9
|
import math
|
10
10
|
import os
|
11
|
-
from typing import Any
|
11
|
+
from typing import Any
|
12
12
|
import uuid
|
13
13
|
|
14
14
|
import yaml
|
@@ -69,10 +69,7 @@ class ESPForceValue:
|
|
69
69
|
pass
|
70
70
|
|
71
71
|
|
72
|
-
def make_data_base(
|
73
|
-
value, from_database: ESPHomeDataBase = None
|
74
|
-
) -> ESPHomeDataBase | Any:
|
75
|
-
"""Wrap a value in a ESPHomeDataBase object."""
|
72
|
+
def make_data_base(value, from_database: ESPHomeDataBase = None):
|
76
73
|
try:
|
77
74
|
value = add_class_to_obj(value, ESPHomeDataBase)
|
78
75
|
if from_database is not None:
|
@@ -105,11 +102,6 @@ def _add_data_ref(fn):
|
|
105
102
|
class ESPHomeLoaderMixin:
|
106
103
|
"""Loader class that keeps track of line numbers."""
|
107
104
|
|
108
|
-
def __init__(self, name: str, yaml_loader: Callable[[str], dict[str, Any]]) -> None:
|
109
|
-
"""Initialize the loader."""
|
110
|
-
self.name = name
|
111
|
-
self.yaml_loader = yaml_loader
|
112
|
-
|
113
105
|
@_add_data_ref
|
114
106
|
def construct_yaml_int(self, node):
|
115
107
|
return super().construct_yaml_int(node)
|
@@ -135,7 +127,7 @@ class ESPHomeLoaderMixin:
|
|
135
127
|
return super().construct_yaml_seq(node)
|
136
128
|
|
137
129
|
@_add_data_ref
|
138
|
-
def construct_yaml_map(self, node
|
130
|
+
def construct_yaml_map(self, node):
|
139
131
|
"""Traverses the given mapping node and returns a list of constructed key-value pairs."""
|
140
132
|
assert isinstance(node, yaml.MappingNode)
|
141
133
|
# A list of key-value pairs we find in the current mapping
|
@@ -239,7 +231,7 @@ class ESPHomeLoaderMixin:
|
|
239
231
|
return OrderedDict(pairs)
|
240
232
|
|
241
233
|
@_add_data_ref
|
242
|
-
def construct_env_var(self, node
|
234
|
+
def construct_env_var(self, node):
|
243
235
|
args = node.value.split()
|
244
236
|
# Check for a default value
|
245
237
|
if len(args) > 1:
|
@@ -251,23 +243,23 @@ class ESPHomeLoaderMixin:
|
|
251
243
|
)
|
252
244
|
|
253
245
|
@property
|
254
|
-
def _directory(self)
|
246
|
+
def _directory(self):
|
255
247
|
return os.path.dirname(self.name)
|
256
248
|
|
257
|
-
def _rel_path(self, *args
|
249
|
+
def _rel_path(self, *args):
|
258
250
|
return os.path.join(self._directory, *args)
|
259
251
|
|
260
252
|
@_add_data_ref
|
261
|
-
def construct_secret(self, node
|
253
|
+
def construct_secret(self, node):
|
262
254
|
try:
|
263
|
-
secrets =
|
255
|
+
secrets = _load_yaml_internal(self._rel_path(SECRET_YAML))
|
264
256
|
except EsphomeError as e:
|
265
257
|
if self.name == CORE.config_path:
|
266
258
|
raise e
|
267
259
|
try:
|
268
260
|
main_config_dir = os.path.dirname(CORE.config_path)
|
269
261
|
main_secret_yml = os.path.join(main_config_dir, SECRET_YAML)
|
270
|
-
secrets =
|
262
|
+
secrets = _load_yaml_internal(main_secret_yml)
|
271
263
|
except EsphomeError as er:
|
272
264
|
raise EsphomeError(f"{e}\n{er}") from er
|
273
265
|
|
@@ -280,9 +272,7 @@ class ESPHomeLoaderMixin:
|
|
280
272
|
return val
|
281
273
|
|
282
274
|
@_add_data_ref
|
283
|
-
def construct_include(
|
284
|
-
self, node: yaml.Node
|
285
|
-
) -> dict[str, Any] | OrderedDict[str, Any]:
|
275
|
+
def construct_include(self, node):
|
286
276
|
from esphome.const import CONF_VARS
|
287
277
|
|
288
278
|
def extract_file_vars(node):
|
@@ -300,93 +290,71 @@ class ESPHomeLoaderMixin:
|
|
300
290
|
else:
|
301
291
|
file, vars = node.value, None
|
302
292
|
|
303
|
-
result =
|
293
|
+
result = _load_yaml_internal(self._rel_path(file))
|
304
294
|
if not vars:
|
305
295
|
vars = {}
|
306
296
|
result = substitute_vars(result, vars)
|
307
297
|
return result
|
308
298
|
|
309
299
|
@_add_data_ref
|
310
|
-
def construct_include_dir_list(self, node
|
300
|
+
def construct_include_dir_list(self, node):
|
311
301
|
files = filter_yaml_files(_find_files(self._rel_path(node.value), "*.yaml"))
|
312
|
-
return [
|
302
|
+
return [_load_yaml_internal(f) for f in files]
|
313
303
|
|
314
304
|
@_add_data_ref
|
315
|
-
def construct_include_dir_merge_list(self, node
|
305
|
+
def construct_include_dir_merge_list(self, node):
|
316
306
|
files = filter_yaml_files(_find_files(self._rel_path(node.value), "*.yaml"))
|
317
307
|
merged_list = []
|
318
308
|
for fname in files:
|
319
|
-
loaded_yaml =
|
309
|
+
loaded_yaml = _load_yaml_internal(fname)
|
320
310
|
if isinstance(loaded_yaml, list):
|
321
311
|
merged_list.extend(loaded_yaml)
|
322
312
|
return merged_list
|
323
313
|
|
324
314
|
@_add_data_ref
|
325
|
-
def construct_include_dir_named(
|
326
|
-
self, node: yaml.Node
|
327
|
-
) -> OrderedDict[str, dict[str, Any]]:
|
315
|
+
def construct_include_dir_named(self, node):
|
328
316
|
files = filter_yaml_files(_find_files(self._rel_path(node.value), "*.yaml"))
|
329
317
|
mapping = OrderedDict()
|
330
318
|
for fname in files:
|
331
319
|
filename = os.path.splitext(os.path.basename(fname))[0]
|
332
|
-
mapping[filename] =
|
320
|
+
mapping[filename] = _load_yaml_internal(fname)
|
333
321
|
return mapping
|
334
322
|
|
335
323
|
@_add_data_ref
|
336
|
-
def construct_include_dir_merge_named(
|
337
|
-
self, node: yaml.Node
|
338
|
-
) -> OrderedDict[str, dict[str, Any]]:
|
324
|
+
def construct_include_dir_merge_named(self, node):
|
339
325
|
files = filter_yaml_files(_find_files(self._rel_path(node.value), "*.yaml"))
|
340
326
|
mapping = OrderedDict()
|
341
327
|
for fname in files:
|
342
|
-
loaded_yaml =
|
328
|
+
loaded_yaml = _load_yaml_internal(fname)
|
343
329
|
if isinstance(loaded_yaml, dict):
|
344
330
|
mapping.update(loaded_yaml)
|
345
331
|
return mapping
|
346
332
|
|
347
333
|
@_add_data_ref
|
348
|
-
def construct_lambda(self, node
|
334
|
+
def construct_lambda(self, node):
|
349
335
|
return Lambda(str(node.value))
|
350
336
|
|
351
337
|
@_add_data_ref
|
352
|
-
def construct_force(self, node
|
338
|
+
def construct_force(self, node):
|
353
339
|
obj = self.construct_scalar(node)
|
354
340
|
return add_class_to_obj(obj, ESPForceValue)
|
355
341
|
|
356
342
|
@_add_data_ref
|
357
|
-
def construct_extend(self, node
|
343
|
+
def construct_extend(self, node):
|
358
344
|
return Extend(str(node.value))
|
359
345
|
|
360
346
|
@_add_data_ref
|
361
|
-
def construct_remove(self, node
|
347
|
+
def construct_remove(self, node):
|
362
348
|
return Remove(str(node.value))
|
363
349
|
|
364
350
|
|
365
351
|
class ESPHomeLoader(ESPHomeLoaderMixin, FastestAvailableSafeLoader):
|
366
352
|
"""Loader class that keeps track of line numbers."""
|
367
353
|
|
368
|
-
def __init__(
|
369
|
-
self,
|
370
|
-
stream: TextIOBase | BytesIO,
|
371
|
-
name: str,
|
372
|
-
yaml_loader: Callable[[str], dict[str, Any]],
|
373
|
-
) -> None:
|
374
|
-
FastestAvailableSafeLoader.__init__(self, stream)
|
375
|
-
ESPHomeLoaderMixin.__init__(self, name, yaml_loader)
|
376
|
-
|
377
354
|
|
378
355
|
class ESPHomePurePythonLoader(ESPHomeLoaderMixin, PurePythonLoader):
|
379
356
|
"""Loader class that keeps track of line numbers."""
|
380
357
|
|
381
|
-
def __init__(
|
382
|
-
self,
|
383
|
-
stream: TextIOBase | BytesIO,
|
384
|
-
name: str,
|
385
|
-
yaml_loader: Callable[[str], dict[str, Any]],
|
386
|
-
) -> None:
|
387
|
-
PurePythonLoader.__init__(self, stream)
|
388
|
-
ESPHomeLoaderMixin.__init__(self, name, yaml_loader)
|
389
|
-
|
390
358
|
|
391
359
|
for _loader in (ESPHomeLoader, ESPHomePurePythonLoader):
|
392
360
|
_loader.add_constructor("tag:yaml.org,2002:int", _loader.construct_yaml_int)
|
@@ -420,30 +388,17 @@ def load_yaml(fname: str, clear_secrets: bool = True) -> Any:
|
|
420
388
|
return _load_yaml_internal(fname)
|
421
389
|
|
422
390
|
|
423
|
-
def
|
424
|
-
"""Load a YAML file."""
|
425
|
-
try:
|
426
|
-
with open(fname, encoding="utf-8") as f_handle:
|
427
|
-
return parse_yaml(fname, f_handle)
|
428
|
-
except (UnicodeDecodeError, OSError) as err:
|
429
|
-
raise EsphomeError(f"Error reading file {fname}: {err}") from err
|
430
|
-
|
431
|
-
|
432
|
-
def parse_yaml(
|
433
|
-
file_name: str, file_handle: TextIOWrapper, yaml_loader=_load_yaml_internal
|
434
|
-
) -> Any:
|
391
|
+
def parse_yaml(file_name: str, file_handle: TextIOWrapper) -> Any:
|
435
392
|
"""Parse a YAML file."""
|
436
393
|
try:
|
437
|
-
return _load_yaml_internal_with_type(
|
438
|
-
ESPHomeLoader, file_name, file_handle, yaml_loader
|
439
|
-
)
|
394
|
+
return _load_yaml_internal_with_type(ESPHomeLoader, file_name, file_handle)
|
440
395
|
except EsphomeError:
|
441
396
|
# Loading failed, so we now load with the Python loader which has more
|
442
397
|
# readable exceptions
|
443
398
|
# Rewind the stream so we can try again
|
444
399
|
file_handle.seek(0, 0)
|
445
400
|
return _load_yaml_internal_with_type(
|
446
|
-
ESPHomePurePythonLoader, file_name, file_handle
|
401
|
+
ESPHomePurePythonLoader, file_name, file_handle
|
447
402
|
)
|
448
403
|
|
449
404
|
|
@@ -480,14 +435,23 @@ def substitute_vars(config, vars):
|
|
480
435
|
return result
|
481
436
|
|
482
437
|
|
438
|
+
def _load_yaml_internal(fname: str) -> Any:
|
439
|
+
"""Load a YAML file."""
|
440
|
+
try:
|
441
|
+
with open(fname, encoding="utf-8") as f_handle:
|
442
|
+
return parse_yaml(fname, f_handle)
|
443
|
+
except (UnicodeDecodeError, OSError) as err:
|
444
|
+
raise EsphomeError(f"Error reading file {fname}: {err}") from err
|
445
|
+
|
446
|
+
|
483
447
|
def _load_yaml_internal_with_type(
|
484
448
|
loader_type: type[ESPHomeLoader] | type[ESPHomePurePythonLoader],
|
485
449
|
fname: str,
|
486
450
|
content: TextIOWrapper,
|
487
|
-
yaml_loader: Any,
|
488
451
|
) -> Any:
|
489
452
|
"""Load a YAML file."""
|
490
|
-
loader = loader_type(content
|
453
|
+
loader = loader_type(content)
|
454
|
+
loader.name = fname
|
491
455
|
try:
|
492
456
|
return loader.get_single_data() or OrderedDict()
|
493
457
|
except yaml.YAMLError as exc:
|
@@ -506,7 +470,7 @@ def dump(dict_, show_secrets=False):
|
|
506
470
|
)
|
507
471
|
|
508
472
|
|
509
|
-
def _is_file_valid(name
|
473
|
+
def _is_file_valid(name):
|
510
474
|
"""Decide if a file is valid."""
|
511
475
|
return not name.startswith(".")
|
512
476
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: esphome
|
3
|
-
Version: 2025.4.
|
3
|
+
Version: 2025.4.0b2
|
4
4
|
Summary: ESPHome is a system to configure your microcontrollers by simple yet powerful configuration files and control them remotely through Home Automation systems.
|
5
5
|
Author-email: The ESPHome Authors <esphome@openhomefoundation.org>
|
6
6
|
License: MIT
|
@@ -39,7 +39,7 @@ Requires-Dist: esptool==4.8.1
|
|
39
39
|
Requires-Dist: click==8.1.7
|
40
40
|
Requires-Dist: esphome-dashboard==20250415.0
|
41
41
|
Requires-Dist: aioesphomeapi==29.10.0
|
42
|
-
Requires-Dist: zeroconf==0.146.
|
42
|
+
Requires-Dist: zeroconf==0.146.4
|
43
43
|
Requires-Dist: puremagic==1.28
|
44
44
|
Requires-Dist: ruamel.yaml==0.18.10
|
45
45
|
Requires-Dist: esphome-glyphsets==0.2.0
|
@@ -1,11 +1,11 @@
|
|
1
1
|
esphome/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
esphome/__main__.py,sha256=
|
2
|
+
esphome/__main__.py,sha256=3PVf9SDEDgPHvXJdGE0dCboamTgRVEWhfmMcY8T3pLw,32587
|
3
3
|
esphome/automation.py,sha256=9xmW3AmWDd2oKB7zF-UITYIiSci8ys8qiylK-rcU7Rg,15689
|
4
4
|
esphome/codegen.py,sha256=GePHUM7xdXb_Pil59SHVsXg2F4VBPgkH-Fz2PDX8Z54,1873
|
5
5
|
esphome/config.py,sha256=fFrDYbhWY1xn_onAl_0jwlg9D8NkK_FdKULTlnjZtxs,39832
|
6
6
|
esphome/config_helpers.py,sha256=MKf_wzO35nn41FvigXE0iYKDslPgL2ruf8R-EPtTT2I,3256
|
7
|
-
esphome/config_validation.py,sha256=
|
8
|
-
esphome/const.py,sha256=
|
7
|
+
esphome/config_validation.py,sha256=9KOhLHQXmDbahg6zHynXfXDfAL2bciu35_SHxHrzZ2M,63705
|
8
|
+
esphome/const.py,sha256=U00foWVg7L2J3h7o4-5e48t8gl8y0deYApkPsFP3dFg,40830
|
9
9
|
esphome/coroutine.py,sha256=j_14z8dIIzIBeuNO30D4c1RJvMMt1xZFZ58Evd-EvJA,9344
|
10
10
|
esphome/cpp_generator.py,sha256=1g-y3fxWSrd5Kpbz6DrJXaQajjuwQiTIaTRIz9n7svI,31237
|
11
11
|
esphome/cpp_helpers.py,sha256=6C2vNbOIhZKi43xRVlk5hp9GfshfBn-rc5D_ZFUEYaE,4801
|
@@ -25,10 +25,10 @@ esphome/storage_json.py,sha256=VyIQDo7hHTIYEeLJTO5cq6I_nOWTlvFVvxqvXd6cgg4,10124
|
|
25
25
|
esphome/types.py,sha256=xJaCRRyYuJiRo32mns9v-YeYB6w12NAT8vMSk9ZmJl8,430
|
26
26
|
esphome/util.py,sha256=sKW1kNMRle3bOGrw9Rga32ZfSDXYMbQeQeVKxFEumJs,9329
|
27
27
|
esphome/voluptuous_schema.py,sha256=Z5nhm8lM4H4D-RB7hi4aCKgPiVLuIZ5EULkC0X4QUjY,9345
|
28
|
-
esphome/vscode.py,sha256=
|
28
|
+
esphome/vscode.py,sha256=612-o7AVyoh1Fmg-ZXYxKVWUc5fcoumsnunpMv7MQuo,3251
|
29
29
|
esphome/wizard.py,sha256=BO89_Y9Z6T8kvC5ZrQGPZzT5Rl9vXpte9L2hqr6CeOk,15349
|
30
30
|
esphome/writer.py,sha256=nJSeC5kZesf9EL4nbsWwhMRq7wPnZPjhP0gLZbVTkvI,10999
|
31
|
-
esphome/yaml_util.py,sha256=
|
31
|
+
esphome/yaml_util.py,sha256=4lCUmBYsbdbxW4yjEAQGcYXhKI3KFkwSzT_N31UojFs,21523
|
32
32
|
esphome/zeroconf.py,sha256=IuAKqtvDCi4uvLKCbLxrP-DFrXVm1I9icbryrCkenAA,6383
|
33
33
|
esphome/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
34
34
|
esphome/components/a01nyub/__init__.py,sha256=d_xNluCS0T63CxdXJGuw7raK7G9vAEwXNJpnA9GthzA,34
|
@@ -143,7 +143,7 @@ esphome/components/alpha3/alpha3.cpp,sha256=1zSC6RmxRxmLFFwRq-pY1BbUWqNiB6ZMOkzf
|
|
143
143
|
esphome/components/alpha3/alpha3.h,sha256=tiCloEGqw9rHdIOocRskj50Z32DiWo6aVUjUk-tRAb8,3405
|
144
144
|
esphome/components/alpha3/sensor.py,sha256=6qSx-_JzQ0wK8aml1LLHqm-u0L0ABX3o8dgqqwdpNvQ,2749
|
145
145
|
esphome/components/am2315c/__init__.py,sha256=_LBsZwU4Hpwdgs3o5uioFVeClSE5fK8k_FZB2MWSADE,30
|
146
|
-
esphome/components/am2315c/am2315c.cpp,sha256=
|
146
|
+
esphome/components/am2315c/am2315c.cpp,sha256=_fQDCMiy6OfTmnWFEz8RG78oygM2SKSvShkp3FLgKaA,5501
|
147
147
|
esphome/components/am2315c/am2315c.h,sha256=xJyLj_vUnDACJ0LehomCzXKWnqWYUmmr9OvjpfdrteQ,2047
|
148
148
|
esphome/components/am2315c/sensor.py,sha256=Y3tt7aOi12IsrfbS9fAHec9BkV0hz3PfmviT-nDyfX0,1702
|
149
149
|
esphome/components/am2320/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -2655,10 +2655,10 @@ esphome/components/sm300d2/sm300d2.h,sha256=YDd_1VQ0Bq0KhWgtvsJLmuXdIRG8purGyxmU
|
|
2655
2655
|
esphome/components/sml/__init__.py,sha256=ibRZmFRQFxOAJ9-YC_00_FYUGQSn2UcFy_9mAKfEg3g,1795
|
2656
2656
|
esphome/components/sml/automation.h,sha256=lY2a8F41GCJB03oPWj4fBJ_3VlXgSc7HKwkVzVmPLqc,411
|
2657
2657
|
esphome/components/sml/constants.h,sha256=bBhl2KSbp7c44Ah4qcVxGwVGMq5na8JgSLwu-Zk-uUE,639
|
2658
|
-
esphome/components/sml/sml.cpp,sha256=
|
2659
|
-
esphome/components/sml/sml.h,sha256=
|
2660
|
-
esphome/components/sml/sml_parser.cpp,sha256=
|
2661
|
-
esphome/components/sml/sml_parser.h,sha256=
|
2658
|
+
esphome/components/sml/sml.cpp,sha256=Th5Q5VbkGt8RsNOCWFDRFFmTbTZj0NH6JRH1A2tSlNE,4209
|
2659
|
+
esphome/components/sml/sml.h,sha256=UWA0WZEwiMk2NWgCm9nUCQyJa6DUSxJwhV_W9EE3l1E,1326
|
2660
|
+
esphome/components/sml/sml_parser.cpp,sha256=Xb7habP5AXdpFn7yJ1tnFAkPxMBTgcDoAI-0FoHD5Fw,5111
|
2661
|
+
esphome/components/sml/sml_parser.h,sha256=9HlTT3dcbn8jnnvwIARwzBC7wasi2UUZrCj6tTXN--E,955
|
2662
2662
|
esphome/components/sml/sensor/__init__.py,sha256=jlZBTGgveDgAtiLdpJzyumU4DiKyZgvTOY6m3q09arg,910
|
2663
2663
|
esphome/components/sml/sensor/sml_sensor.cpp,sha256=onWh6gi584Qy8TaYgnRi5K4nMUkS39P8_liCTTKW5JM,1106
|
2664
2664
|
esphome/components/sml/sensor/sml_sensor.h,sha256=QypjAdqlNPIVLP77YuxASVRJVwjITCuoc5q0MvzUjNY,420
|
@@ -3423,7 +3423,7 @@ esphome/components/zyaura/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
|
|
3423
3423
|
esphome/components/zyaura/sensor.py,sha256=cSmO4ozYdi4ZD7NK4lmYjswWVmJoDvEruU1HhGrZSE0,2476
|
3424
3424
|
esphome/components/zyaura/zyaura.cpp,sha256=F7WM8XAZ5MYuCD3eERm2_IA-O7sP1i-A-yF5TV4PqX8,3679
|
3425
3425
|
esphome/components/zyaura/zyaura.h,sha256=yWrcYj_CnLDyopkuIVySJy5dB-R5X9zwm5fWjBDTSts,2384
|
3426
|
-
esphome/core/__init__.py,sha256=
|
3426
|
+
esphome/core/__init__.py,sha256=L4pbrSzT5rq3ba3Xm1N5xiRx4m5a4pKN5wBwvUwWZLU,26760
|
3427
3427
|
esphome/core/application.cpp,sha256=DOhq0rTYu_dNcUCavHYVYASQZ5QzVOXMWVTaO7YTdnM,4680
|
3428
3428
|
esphome/core/application.h,sha256=jepd6I9BBa48ByAXoLnUE7ddtlHiUS5uuKaMaJOD0x0,17721
|
3429
3429
|
esphome/core/automation.h,sha256=UQQD9De3eiaopvzYQG89SxkBfnL5QaiR6bvkk2RxV8k,7332
|
@@ -3481,9 +3481,9 @@ esphome/dashboard/util/itertools.py,sha256=8eLrWEWmICLtXNxkKdYPQV0c_N4GEz8m9Npnb
|
|
3481
3481
|
esphome/dashboard/util/password.py,sha256=cQz3b9B-ijTe7zS6BeCW0hc3pWv6JjC78jmnycYYAh8,321
|
3482
3482
|
esphome/dashboard/util/subprocess.py,sha256=T8EW6dbU4LPd2DG1dRrdh8li71tt6J1isn411poMhkk,1022
|
3483
3483
|
esphome/dashboard/util/text.py,sha256=ENDnfN4O0NdA3CKVJjQYabFbwbrsIhVKrAMQe53qYu4,534
|
3484
|
-
esphome-2025.4.
|
3485
|
-
esphome-2025.4.
|
3486
|
-
esphome-2025.4.
|
3487
|
-
esphome-2025.4.
|
3488
|
-
esphome-2025.4.
|
3489
|
-
esphome-2025.4.
|
3484
|
+
esphome-2025.4.0b2.dist-info/licenses/LICENSE,sha256=HzEjkBInJe44L4WvAOPfhPJJDNj6YbnqFyvGWRzArGM,36664
|
3485
|
+
esphome-2025.4.0b2.dist-info/METADATA,sha256=QDzQqlrluTWB6o0YzPZysWvsFSvlhigg7_zgG0ifL34,3673
|
3486
|
+
esphome-2025.4.0b2.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
3487
|
+
esphome-2025.4.0b2.dist-info/entry_points.txt,sha256=mIxVNuWtbYzeEcaWCl-AQ-97aBOWbnYBAK8nbF6P4M0,50
|
3488
|
+
esphome-2025.4.0b2.dist-info/top_level.txt,sha256=0GSXEW3cnITpgG3qnsSMz0qoqJHAFyfw7Y8MVtEf1Yk,8
|
3489
|
+
esphome-2025.4.0b2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|