esphome 2025.5.0b2__py3-none-any.whl → 2025.5.0b4__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 +16 -14
- esphome/components/api/api_connection.cpp +339 -652
- esphome/components/api/api_connection.h +251 -57
- esphome/components/api/api_frame_helper.cpp +136 -49
- esphome/components/api/api_frame_helper.h +49 -6
- esphome/components/api/proto.h +48 -8
- esphome/components/ballu/climate.py +1 -1
- esphome/components/bedjet/bedjet_hub.cpp +1 -0
- esphome/components/binary_sensor/binary_sensor.cpp +10 -6
- esphome/components/binary_sensor/binary_sensor.h +1 -1
- esphome/components/binary_sensor/filter.cpp +21 -21
- esphome/components/binary_sensor/filter.h +10 -10
- esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +2 -1
- esphome/components/ccs811/sensor.py +9 -6
- esphome/components/climate_ir/__init__.py +3 -3
- esphome/components/climate_ir_lg/climate.py +1 -1
- esphome/components/coolix/climate.py +1 -1
- esphome/components/cse7766/cse7766.cpp +2 -1
- esphome/components/current_based/current_based_cover.cpp +2 -1
- esphome/components/daikin/climate.py +1 -1
- esphome/components/daikin_arc/climate.py +1 -1
- esphome/components/daikin_brc/climate.py +1 -1
- esphome/components/daly_bms/daly_bms.cpp +2 -1
- esphome/components/debug/debug_component.cpp +1 -1
- esphome/components/delonghi/climate.py +1 -1
- esphome/components/dps310/sensor.py +6 -6
- esphome/components/ee895/sensor.py +9 -9
- esphome/components/emmeti/climate.py +1 -1
- esphome/components/endstop/endstop_cover.cpp +2 -1
- esphome/components/ens160_base/__init__.py +12 -9
- esphome/components/esp32_ble/ble_advertising.cpp +2 -1
- esphome/components/esp32_camera/__init__.py +1 -1
- esphome/components/esp32_camera/esp32_camera.cpp +2 -10
- esphome/components/esp32_camera/esp32_camera.h +1 -1
- esphome/components/esp32_improv/esp32_improv_component.cpp +1 -1
- esphome/components/esp32_touch/esp32_touch.cpp +1 -1
- esphome/components/ethernet/ethernet_component.cpp +1 -1
- esphome/components/feedback/feedback_cover.cpp +2 -1
- esphome/components/fujitsu_general/climate.py +1 -1
- esphome/components/gcja5/gcja5.cpp +2 -1
- esphome/components/gps/__init__.py +37 -16
- esphome/components/gps/gps.cpp +33 -17
- esphome/components/gps/gps.h +16 -15
- esphome/components/gree/climate.py +1 -1
- esphome/components/growatt_solar/growatt_solar.cpp +2 -1
- esphome/components/heatpumpir/climate.py +1 -1
- esphome/components/hitachi_ac344/climate.py +1 -1
- esphome/components/hitachi_ac424/climate.py +1 -1
- esphome/components/hte501/sensor.py +6 -6
- esphome/components/hyt271/sensor.py +6 -6
- esphome/components/kuntze/kuntze.cpp +2 -1
- esphome/components/logger/__init__.py +1 -0
- esphome/components/logger/logger.cpp +53 -32
- esphome/components/logger/logger.h +55 -5
- esphome/components/matrix_keypad/matrix_keypad.cpp +2 -1
- esphome/components/max7219digit/max7219digit.cpp +2 -1
- esphome/components/mhz19/sensor.py +11 -7
- esphome/components/midea_ir/climate.py +1 -1
- esphome/components/mitsubishi/climate.py +1 -1
- esphome/components/modbus/modbus.cpp +2 -1
- esphome/components/mqtt/mqtt_client.cpp +1 -1
- esphome/components/ms5611/sensor.py +6 -6
- esphome/components/ms8607/sensor.py +3 -3
- esphome/components/noblex/climate.py +1 -1
- esphome/components/pmsx003/pmsx003.cpp +2 -1
- esphome/components/pzem004t/pzem004t.cpp +2 -1
- esphome/components/rf_bridge/rf_bridge.cpp +2 -1
- esphome/components/sds011/sds011.cpp +2 -1
- esphome/components/sen5x/sen5x.cpp +55 -36
- esphome/components/senseair/sensor.py +3 -3
- esphome/components/sgp30/sensor.py +14 -16
- esphome/components/shtcx/sensor.py +6 -6
- esphome/components/slow_pwm/slow_pwm_output.cpp +2 -1
- esphome/components/sprinkler/sprinkler.cpp +6 -5
- esphome/components/t6615/sensor.py +3 -3
- esphome/components/t6615/t6615.cpp +2 -1
- esphome/components/tcl112/climate.py +1 -1
- esphome/components/time_based/time_based_cover.cpp +2 -1
- esphome/components/toshiba/climate.py +1 -1
- esphome/components/uart/switch/uart_switch.cpp +2 -1
- esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +2 -1
- esphome/components/uponor_smatrix/uponor_smatrix.cpp +2 -1
- esphome/components/weikai/weikai.cpp +0 -52
- esphome/components/whirlpool/climate.py +1 -1
- esphome/components/whynter/climate.py +1 -1
- esphome/components/zhlt01/climate.py +1 -1
- esphome/config.py +13 -13
- esphome/const.py +1 -1
- esphome/core/application.cpp +26 -10
- esphome/core/application.h +5 -1
- esphome/core/component.cpp +10 -5
- esphome/core/component.h +5 -1
- esphome/core/doxygen.h +13 -0
- esphome/core/scheduler.cpp +4 -1
- esphome/log.py +15 -19
- esphome/mqtt.py +2 -2
- esphome/voluptuous_schema.py +3 -1
- esphome/wizard.py +45 -35
- {esphome-2025.5.0b2.dist-info → esphome-2025.5.0b4.dist-info}/METADATA +1 -1
- {esphome-2025.5.0b2.dist-info → esphome-2025.5.0b4.dist-info}/RECORD +104 -103
- {esphome-2025.5.0b2.dist-info → esphome-2025.5.0b4.dist-info}/WHEEL +0 -0
- {esphome-2025.5.0b2.dist-info → esphome-2025.5.0b4.dist-info}/entry_points.txt +0 -0
- {esphome-2025.5.0b2.dist-info → esphome-2025.5.0b4.dist-info}/licenses/LICENSE +0 -0
- {esphome-2025.5.0b2.dist-info → esphome-2025.5.0b4.dist-info}/top_level.txt +0 -0
@@ -9,23 +9,32 @@ from esphome.const import (
|
|
9
9
|
CONF_LONGITUDE,
|
10
10
|
CONF_SATELLITES,
|
11
11
|
CONF_SPEED,
|
12
|
+
DEVICE_CLASS_SPEED,
|
12
13
|
STATE_CLASS_MEASUREMENT,
|
13
14
|
UNIT_DEGREES,
|
14
15
|
UNIT_KILOMETER_PER_HOUR,
|
15
16
|
UNIT_METER,
|
16
17
|
)
|
17
18
|
|
19
|
+
CONF_GPS_ID = "gps_id"
|
20
|
+
CONF_HDOP = "hdop"
|
21
|
+
|
22
|
+
ICON_ALTIMETER = "mdi:altimeter"
|
23
|
+
ICON_COMPASS = "mdi:compass"
|
24
|
+
ICON_LATITUDE = "mdi:latitude"
|
25
|
+
ICON_LONGITUDE = "mdi:longitude"
|
26
|
+
ICON_SATELLITE = "mdi:satellite-variant"
|
27
|
+
ICON_SPEEDOMETER = "mdi:speedometer"
|
28
|
+
|
18
29
|
DEPENDENCIES = ["uart"]
|
19
30
|
AUTO_LOAD = ["sensor"]
|
20
31
|
|
21
|
-
CODEOWNERS = ["@coogle"]
|
32
|
+
CODEOWNERS = ["@coogle", "@ximex"]
|
22
33
|
|
23
34
|
gps_ns = cg.esphome_ns.namespace("gps")
|
24
35
|
GPS = gps_ns.class_("GPS", cg.Component, uart.UARTDevice)
|
25
36
|
GPSListener = gps_ns.class_("GPSListener")
|
26
37
|
|
27
|
-
CONF_GPS_ID = "gps_id"
|
28
|
-
CONF_HDOP = "hdop"
|
29
38
|
MULTI_CONF = True
|
30
39
|
CONFIG_SCHEMA = cv.All(
|
31
40
|
cv.Schema(
|
@@ -33,25 +42,37 @@ CONFIG_SCHEMA = cv.All(
|
|
33
42
|
cv.GenerateID(): cv.declare_id(GPS),
|
34
43
|
cv.Optional(CONF_LATITUDE): sensor.sensor_schema(
|
35
44
|
unit_of_measurement=UNIT_DEGREES,
|
45
|
+
icon=ICON_LATITUDE,
|
36
46
|
accuracy_decimals=6,
|
47
|
+
state_class=STATE_CLASS_MEASUREMENT,
|
37
48
|
),
|
38
49
|
cv.Optional(CONF_LONGITUDE): sensor.sensor_schema(
|
39
50
|
unit_of_measurement=UNIT_DEGREES,
|
51
|
+
icon=ICON_LONGITUDE,
|
40
52
|
accuracy_decimals=6,
|
53
|
+
state_class=STATE_CLASS_MEASUREMENT,
|
41
54
|
),
|
42
55
|
cv.Optional(CONF_SPEED): sensor.sensor_schema(
|
43
56
|
unit_of_measurement=UNIT_KILOMETER_PER_HOUR,
|
57
|
+
icon=ICON_SPEEDOMETER,
|
44
58
|
accuracy_decimals=3,
|
59
|
+
device_class=DEVICE_CLASS_SPEED,
|
60
|
+
state_class=STATE_CLASS_MEASUREMENT,
|
45
61
|
),
|
46
62
|
cv.Optional(CONF_COURSE): sensor.sensor_schema(
|
47
63
|
unit_of_measurement=UNIT_DEGREES,
|
64
|
+
icon=ICON_COMPASS,
|
48
65
|
accuracy_decimals=2,
|
66
|
+
state_class=STATE_CLASS_MEASUREMENT,
|
49
67
|
),
|
50
68
|
cv.Optional(CONF_ALTITUDE): sensor.sensor_schema(
|
51
69
|
unit_of_measurement=UNIT_METER,
|
70
|
+
icon=ICON_ALTIMETER,
|
52
71
|
accuracy_decimals=2,
|
72
|
+
state_class=STATE_CLASS_MEASUREMENT,
|
53
73
|
),
|
54
74
|
cv.Optional(CONF_SATELLITES): sensor.sensor_schema(
|
75
|
+
icon=ICON_SATELLITE,
|
55
76
|
accuracy_decimals=0,
|
56
77
|
state_class=STATE_CLASS_MEASUREMENT,
|
57
78
|
),
|
@@ -73,28 +94,28 @@ async def to_code(config):
|
|
73
94
|
await cg.register_component(var, config)
|
74
95
|
await uart.register_uart_device(var, config)
|
75
96
|
|
76
|
-
if
|
77
|
-
sens = await sensor.new_sensor(
|
97
|
+
if latitude_config := config.get(CONF_LATITUDE):
|
98
|
+
sens = await sensor.new_sensor(latitude_config)
|
78
99
|
cg.add(var.set_latitude_sensor(sens))
|
79
100
|
|
80
|
-
if
|
81
|
-
sens = await sensor.new_sensor(
|
101
|
+
if longitude_config := config.get(CONF_LONGITUDE):
|
102
|
+
sens = await sensor.new_sensor(longitude_config)
|
82
103
|
cg.add(var.set_longitude_sensor(sens))
|
83
104
|
|
84
|
-
if
|
85
|
-
sens = await sensor.new_sensor(
|
105
|
+
if speed_config := config.get(CONF_SPEED):
|
106
|
+
sens = await sensor.new_sensor(speed_config)
|
86
107
|
cg.add(var.set_speed_sensor(sens))
|
87
108
|
|
88
|
-
if
|
89
|
-
sens = await sensor.new_sensor(
|
109
|
+
if course_config := config.get(CONF_COURSE):
|
110
|
+
sens = await sensor.new_sensor(course_config)
|
90
111
|
cg.add(var.set_course_sensor(sens))
|
91
112
|
|
92
|
-
if
|
93
|
-
sens = await sensor.new_sensor(
|
113
|
+
if altitude_config := config.get(CONF_ALTITUDE):
|
114
|
+
sens = await sensor.new_sensor(altitude_config)
|
94
115
|
cg.add(var.set_altitude_sensor(sens))
|
95
116
|
|
96
|
-
if
|
97
|
-
sens = await sensor.new_sensor(
|
117
|
+
if satellites_config := config.get(CONF_SATELLITES):
|
118
|
+
sens = await sensor.new_sensor(satellites_config)
|
98
119
|
cg.add(var.set_satellites_sensor(sens))
|
99
120
|
|
100
121
|
if hdop_config := config.get(CONF_HDOP):
|
@@ -102,4 +123,4 @@ async def to_code(config):
|
|
102
123
|
cg.add(var.set_hdop_sensor(sens))
|
103
124
|
|
104
125
|
# https://platformio.org/lib/show/1655/TinyGPSPlus
|
105
|
-
cg.add_library("mikalhart/TinyGPSPlus", "1.0
|
126
|
+
cg.add_library("mikalhart/TinyGPSPlus", "1.1.0")
|
esphome/components/gps/gps.cpp
CHANGED
@@ -10,6 +10,17 @@ static const char *const TAG = "gps";
|
|
10
10
|
|
11
11
|
TinyGPSPlus &GPSListener::get_tiny_gps() { return this->parent_->get_tiny_gps(); }
|
12
12
|
|
13
|
+
void GPS::dump_config() {
|
14
|
+
ESP_LOGCONFIG(TAG, "GPS:");
|
15
|
+
LOG_SENSOR(" ", "Latitude", this->latitude_sensor_);
|
16
|
+
LOG_SENSOR(" ", "Longitude", this->longitude_sensor_);
|
17
|
+
LOG_SENSOR(" ", "Speed", this->speed_sensor_);
|
18
|
+
LOG_SENSOR(" ", "Course", this->course_sensor_);
|
19
|
+
LOG_SENSOR(" ", "Altitude", this->altitude_sensor_);
|
20
|
+
LOG_SENSOR(" ", "Satellites", this->satellites_sensor_);
|
21
|
+
LOG_SENSOR(" ", "HDOP", this->hdop_sensor_);
|
22
|
+
}
|
23
|
+
|
13
24
|
void GPS::update() {
|
14
25
|
if (this->latitude_sensor_ != nullptr)
|
15
26
|
this->latitude_sensor_->publish_state(this->latitude_);
|
@@ -34,40 +45,45 @@ void GPS::update() {
|
|
34
45
|
}
|
35
46
|
|
36
47
|
void GPS::loop() {
|
37
|
-
while (this->available() && !this->has_time_) {
|
48
|
+
while (this->available() > 0 && !this->has_time_) {
|
38
49
|
if (this->tiny_gps_.encode(this->read())) {
|
39
|
-
if (tiny_gps_.location.isUpdated()) {
|
40
|
-
this->latitude_ = tiny_gps_.location.lat();
|
41
|
-
this->longitude_ = tiny_gps_.location.lng();
|
50
|
+
if (this->tiny_gps_.location.isUpdated()) {
|
51
|
+
this->latitude_ = this->tiny_gps_.location.lat();
|
52
|
+
this->longitude_ = this->tiny_gps_.location.lng();
|
42
53
|
|
43
54
|
ESP_LOGD(TAG, "Location:");
|
44
|
-
ESP_LOGD(TAG, " Lat:
|
45
|
-
ESP_LOGD(TAG, " Lon:
|
55
|
+
ESP_LOGD(TAG, " Lat: %.6f °", this->latitude_);
|
56
|
+
ESP_LOGD(TAG, " Lon: %.6f °", this->longitude_);
|
46
57
|
}
|
47
58
|
|
48
|
-
if (tiny_gps_.speed.isUpdated()) {
|
49
|
-
this->speed_ = tiny_gps_.speed.kmph();
|
59
|
+
if (this->tiny_gps_.speed.isUpdated()) {
|
60
|
+
this->speed_ = this->tiny_gps_.speed.kmph();
|
50
61
|
ESP_LOGD(TAG, "Speed: %.3f km/h", this->speed_);
|
51
62
|
}
|
52
|
-
|
53
|
-
|
63
|
+
|
64
|
+
if (this->tiny_gps_.course.isUpdated()) {
|
65
|
+
this->course_ = this->tiny_gps_.course.deg();
|
54
66
|
ESP_LOGD(TAG, "Course: %.2f °", this->course_);
|
55
67
|
}
|
56
|
-
|
57
|
-
|
68
|
+
|
69
|
+
if (this->tiny_gps_.altitude.isUpdated()) {
|
70
|
+
this->altitude_ = this->tiny_gps_.altitude.meters();
|
58
71
|
ESP_LOGD(TAG, "Altitude: %.2f m", this->altitude_);
|
59
72
|
}
|
60
|
-
|
61
|
-
|
73
|
+
|
74
|
+
if (this->tiny_gps_.satellites.isUpdated()) {
|
75
|
+
this->satellites_ = this->tiny_gps_.satellites.value();
|
62
76
|
ESP_LOGD(TAG, "Satellites: %d", this->satellites_);
|
63
77
|
}
|
64
|
-
|
65
|
-
|
78
|
+
|
79
|
+
if (this->tiny_gps_.hdop.isUpdated()) {
|
80
|
+
this->hdop_ = this->tiny_gps_.hdop.hdop();
|
66
81
|
ESP_LOGD(TAG, "HDOP: %.3f", this->hdop_);
|
67
82
|
}
|
68
83
|
|
69
|
-
for (auto *listener : this->listeners_)
|
84
|
+
for (auto *listener : this->listeners_) {
|
70
85
|
listener->on_update(this->tiny_gps_);
|
86
|
+
}
|
71
87
|
}
|
72
88
|
}
|
73
89
|
}
|
esphome/components/gps/gps.h
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
#include "esphome/core/component.h"
|
6
6
|
#include "esphome/components/uart/uart.h"
|
7
7
|
#include "esphome/components/sensor/sensor.h"
|
8
|
-
#include <
|
8
|
+
#include <TinyGPSPlus.h>
|
9
9
|
|
10
10
|
#include <vector>
|
11
11
|
|
@@ -27,13 +27,13 @@ class GPSListener {
|
|
27
27
|
|
28
28
|
class GPS : public PollingComponent, public uart::UARTDevice {
|
29
29
|
public:
|
30
|
-
void set_latitude_sensor(sensor::Sensor *latitude_sensor) { latitude_sensor_ = latitude_sensor; }
|
31
|
-
void set_longitude_sensor(sensor::Sensor *longitude_sensor) { longitude_sensor_ = longitude_sensor; }
|
32
|
-
void set_speed_sensor(sensor::Sensor *speed_sensor) { speed_sensor_ = speed_sensor; }
|
33
|
-
void set_course_sensor(sensor::Sensor *course_sensor) { course_sensor_ = course_sensor; }
|
34
|
-
void set_altitude_sensor(sensor::Sensor *altitude_sensor) { altitude_sensor_ = altitude_sensor; }
|
35
|
-
void set_satellites_sensor(sensor::Sensor *satellites_sensor) { satellites_sensor_ = satellites_sensor; }
|
36
|
-
void set_hdop_sensor(sensor::Sensor *hdop_sensor) { hdop_sensor_ = hdop_sensor; }
|
30
|
+
void set_latitude_sensor(sensor::Sensor *latitude_sensor) { this->latitude_sensor_ = latitude_sensor; }
|
31
|
+
void set_longitude_sensor(sensor::Sensor *longitude_sensor) { this->longitude_sensor_ = longitude_sensor; }
|
32
|
+
void set_speed_sensor(sensor::Sensor *speed_sensor) { this->speed_sensor_ = speed_sensor; }
|
33
|
+
void set_course_sensor(sensor::Sensor *course_sensor) { this->course_sensor_ = course_sensor; }
|
34
|
+
void set_altitude_sensor(sensor::Sensor *altitude_sensor) { this->altitude_sensor_ = altitude_sensor; }
|
35
|
+
void set_satellites_sensor(sensor::Sensor *satellites_sensor) { this->satellites_sensor_ = satellites_sensor; }
|
36
|
+
void set_hdop_sensor(sensor::Sensor *hdop_sensor) { this->hdop_sensor_ = hdop_sensor; }
|
37
37
|
|
38
38
|
void register_listener(GPSListener *listener) {
|
39
39
|
listener->parent_ = this;
|
@@ -41,19 +41,20 @@ class GPS : public PollingComponent, public uart::UARTDevice {
|
|
41
41
|
}
|
42
42
|
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
43
43
|
|
44
|
+
void dump_config() override;
|
44
45
|
void loop() override;
|
45
46
|
void update() override;
|
46
47
|
|
47
48
|
TinyGPSPlus &get_tiny_gps() { return this->tiny_gps_; }
|
48
49
|
|
49
50
|
protected:
|
50
|
-
float latitude_
|
51
|
-
float longitude_
|
52
|
-
float speed_
|
53
|
-
float course_
|
54
|
-
float altitude_
|
55
|
-
|
56
|
-
|
51
|
+
float latitude_{NAN};
|
52
|
+
float longitude_{NAN};
|
53
|
+
float speed_{NAN};
|
54
|
+
float course_{NAN};
|
55
|
+
float altitude_{NAN};
|
56
|
+
uint16_t satellites_{0};
|
57
|
+
float hdop_{NAN};
|
57
58
|
|
58
59
|
sensor::Sensor *latitude_sensor_{nullptr};
|
59
60
|
sensor::Sensor *longitude_sensor_{nullptr};
|
@@ -21,7 +21,7 @@ MODELS = {
|
|
21
21
|
"yag": Model.GREE_YAG,
|
22
22
|
}
|
23
23
|
|
24
|
-
CONFIG_SCHEMA = climate_ir.
|
24
|
+
CONFIG_SCHEMA = climate_ir.climate_ir_with_receiver_schema(GreeClimate).extend(
|
25
25
|
{
|
26
26
|
cv.Required(CONF_MODEL): cv.enum(MODELS),
|
27
27
|
}
|
@@ -1,5 +1,6 @@
|
|
1
1
|
#include "growatt_solar.h"
|
2
2
|
#include "esphome/core/log.h"
|
3
|
+
#include "esphome/core/application.h"
|
3
4
|
|
4
5
|
namespace esphome {
|
5
6
|
namespace growatt_solar {
|
@@ -18,7 +19,7 @@ void GrowattSolar::loop() {
|
|
18
19
|
|
19
20
|
void GrowattSolar::update() {
|
20
21
|
// If our last send has had no reply yet, and it wasn't that long ago, do nothing.
|
21
|
-
uint32_t now =
|
22
|
+
const uint32_t now = App.get_loop_component_start_time();
|
22
23
|
if (now - this->last_send_ < this->get_update_interval() / 2) {
|
23
24
|
return;
|
24
25
|
}
|
@@ -97,7 +97,7 @@ VERTICAL_DIRECTIONS = {
|
|
97
97
|
}
|
98
98
|
|
99
99
|
CONFIG_SCHEMA = cv.All(
|
100
|
-
climate_ir.
|
100
|
+
climate_ir.climate_ir_with_receiver_schema(HeatpumpIRClimate).extend(
|
101
101
|
{
|
102
102
|
cv.Required(CONF_PROTOCOL): cv.enum(PROTOCOLS),
|
103
103
|
cv.Required(CONF_HORIZONTAL_DEFAULT): cv.enum(HORIZONTAL_DIRECTIONS),
|
@@ -6,7 +6,7 @@ AUTO_LOAD = ["climate_ir"]
|
|
6
6
|
hitachi_ac344_ns = cg.esphome_ns.namespace("hitachi_ac344")
|
7
7
|
HitachiClimate = hitachi_ac344_ns.class_("HitachiClimate", climate_ir.ClimateIR)
|
8
8
|
|
9
|
-
CONFIG_SCHEMA = climate_ir.
|
9
|
+
CONFIG_SCHEMA = climate_ir.climate_ir_with_receiver_schema(HitachiClimate)
|
10
10
|
|
11
11
|
|
12
12
|
async def to_code(config):
|
@@ -6,7 +6,7 @@ AUTO_LOAD = ["climate_ir"]
|
|
6
6
|
hitachi_ac424_ns = cg.esphome_ns.namespace("hitachi_ac424")
|
7
7
|
HitachiClimate = hitachi_ac424_ns.class_("HitachiClimate", climate_ir.ClimateIR)
|
8
8
|
|
9
|
-
CONFIG_SCHEMA = climate_ir.
|
9
|
+
CONFIG_SCHEMA = climate_ir.climate_ir_with_receiver_schema(HitachiClimate)
|
10
10
|
|
11
11
|
|
12
12
|
async def to_code(config):
|
@@ -25,13 +25,13 @@ CONFIG_SCHEMA = (
|
|
25
25
|
cv.Schema(
|
26
26
|
{
|
27
27
|
cv.GenerateID(): cv.declare_id(HTE501Component),
|
28
|
-
cv.
|
28
|
+
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
29
29
|
unit_of_measurement=UNIT_CELSIUS,
|
30
30
|
accuracy_decimals=1,
|
31
31
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
32
32
|
state_class=STATE_CLASS_MEASUREMENT,
|
33
33
|
),
|
34
|
-
cv.
|
34
|
+
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
35
35
|
unit_of_measurement=UNIT_PERCENT,
|
36
36
|
accuracy_decimals=1,
|
37
37
|
device_class=DEVICE_CLASS_HUMIDITY,
|
@@ -49,10 +49,10 @@ async def to_code(config):
|
|
49
49
|
await cg.register_component(var, config)
|
50
50
|
await i2c.register_i2c_device(var, config)
|
51
51
|
|
52
|
-
if
|
53
|
-
sens = await sensor.new_sensor(
|
52
|
+
if temperature := config.get(CONF_TEMPERATURE):
|
53
|
+
sens = await sensor.new_sensor(temperature)
|
54
54
|
cg.add(var.set_temperature_sensor(sens))
|
55
55
|
|
56
|
-
if
|
57
|
-
sens = await sensor.new_sensor(
|
56
|
+
if humidity := config.get(CONF_HUMIDITY):
|
57
|
+
sens = await sensor.new_sensor(humidity)
|
58
58
|
cg.add(var.set_humidity_sensor(sens))
|
@@ -23,13 +23,13 @@ CONFIG_SCHEMA = (
|
|
23
23
|
cv.Schema(
|
24
24
|
{
|
25
25
|
cv.GenerateID(): cv.declare_id(HYT271Component),
|
26
|
-
cv.
|
26
|
+
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
27
27
|
unit_of_measurement=UNIT_CELSIUS,
|
28
28
|
accuracy_decimals=1,
|
29
29
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
30
30
|
state_class=STATE_CLASS_MEASUREMENT,
|
31
31
|
),
|
32
|
-
cv.
|
32
|
+
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
33
33
|
unit_of_measurement=UNIT_PERCENT,
|
34
34
|
accuracy_decimals=1,
|
35
35
|
device_class=DEVICE_CLASS_HUMIDITY,
|
@@ -47,10 +47,10 @@ async def to_code(config):
|
|
47
47
|
await cg.register_component(var, config)
|
48
48
|
await i2c.register_i2c_device(var, config)
|
49
49
|
|
50
|
-
if
|
51
|
-
sens = await sensor.new_sensor(
|
50
|
+
if temperature := config.get(CONF_TEMPERATURE):
|
51
|
+
sens = await sensor.new_sensor(temperature)
|
52
52
|
cg.add(var.set_temperature(sens))
|
53
53
|
|
54
|
-
if
|
55
|
-
sens = await sensor.new_sensor(
|
54
|
+
if humidity := config.get(CONF_HUMIDITY):
|
55
|
+
sens = await sensor.new_sensor(humidity)
|
56
56
|
cg.add(var.set_humidity(sens))
|
@@ -1,5 +1,6 @@
|
|
1
1
|
#include "kuntze.h"
|
2
2
|
#include "esphome/core/log.h"
|
3
|
+
#include "esphome/core/application.h"
|
3
4
|
|
4
5
|
namespace esphome {
|
5
6
|
namespace kuntze {
|
@@ -60,7 +61,7 @@ void Kuntze::on_modbus_data(const std::vector<uint8_t> &data) {
|
|
60
61
|
}
|
61
62
|
|
62
63
|
void Kuntze::loop() {
|
63
|
-
uint32_t now =
|
64
|
+
uint32_t now = App.get_loop_component_start_time();
|
64
65
|
// timeout after 15 seconds
|
65
66
|
if (this->waiting_ && (now - this->last_send_ > 15000)) {
|
66
67
|
ESP_LOGW(TAG, "timed out waiting for response");
|
@@ -254,6 +254,7 @@ async def to_code(config):
|
|
254
254
|
config[CONF_TX_BUFFER_SIZE],
|
255
255
|
)
|
256
256
|
if CORE.is_esp32:
|
257
|
+
cg.add(log.create_pthread_key())
|
257
258
|
task_log_buffer_size = config[CONF_TASK_LOG_BUFFER_SIZE]
|
258
259
|
if task_log_buffer_size > 0:
|
259
260
|
cg.add_define("USE_ESPHOME_TASK_LOG_BUFFER")
|
@@ -14,25 +14,47 @@ namespace logger {
|
|
14
14
|
static const char *const TAG = "logger";
|
15
15
|
|
16
16
|
#ifdef USE_ESP32
|
17
|
-
// Implementation for ESP32 (multi-
|
18
|
-
// Main
|
19
|
-
//
|
17
|
+
// Implementation for ESP32 (multi-task platform with task-specific tracking)
|
18
|
+
// Main task always uses direct buffer access for console output and callbacks
|
19
|
+
//
|
20
|
+
// For non-main tasks:
|
21
|
+
// - WITH task log buffer: Prefer sending to ring buffer for async processing
|
22
|
+
// - Avoids allocating stack memory for console output in normal operation
|
23
|
+
// - Prevents console corruption from concurrent writes by multiple tasks
|
24
|
+
// - Messages are serialized through main loop for proper console output
|
25
|
+
// - Fallback to emergency console logging only if ring buffer is full
|
26
|
+
// - WITHOUT task log buffer: Only emergency console output, no callbacks
|
20
27
|
void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *format, va_list args) { // NOLINT
|
21
|
-
if (level > this->level_for(tag)
|
28
|
+
if (level > this->level_for(tag))
|
22
29
|
return;
|
23
|
-
recursion_guard_.store(true, std::memory_order_relaxed);
|
24
30
|
|
25
31
|
TaskHandle_t current_task = xTaskGetCurrentTaskHandle();
|
32
|
+
bool is_main_task = (current_task == main_task_);
|
26
33
|
|
27
|
-
//
|
28
|
-
if (
|
34
|
+
// Check and set recursion guard - uses pthread TLS for per-task state
|
35
|
+
if (this->check_and_set_task_log_recursion_(is_main_task)) {
|
36
|
+
return; // Recursion detected
|
37
|
+
}
|
38
|
+
|
39
|
+
// Main task uses the shared buffer for efficiency
|
40
|
+
if (is_main_task) {
|
29
41
|
this->log_message_to_buffer_and_send_(level, tag, line, format, args);
|
30
|
-
|
42
|
+
this->reset_task_log_recursion_(is_main_task);
|
31
43
|
return;
|
32
44
|
}
|
33
45
|
|
34
|
-
|
35
|
-
|
46
|
+
bool message_sent = false;
|
47
|
+
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
|
48
|
+
// For non-main tasks, queue the message for callbacks - but only if we have any callbacks registered
|
49
|
+
message_sent = this->log_buffer_->send_message_thread_safe(static_cast<uint8_t>(level), tag,
|
50
|
+
static_cast<uint16_t>(line), current_task, format, args);
|
51
|
+
#endif // USE_ESPHOME_TASK_LOG_BUFFER
|
52
|
+
|
53
|
+
// Emergency console logging for non-main tasks when ring buffer is full or disabled
|
54
|
+
// This is a fallback mechanism to ensure critical log messages are visible
|
55
|
+
// Note: This may cause interleaved/corrupted console output if multiple tasks
|
56
|
+
// log simultaneously, but it's better than losing important messages entirely
|
57
|
+
if (!message_sent && this->baud_rate_ > 0) { // If logging is enabled, write to console
|
36
58
|
// Maximum size for console log messages (includes null terminator)
|
37
59
|
static const size_t MAX_CONSOLE_LOG_MSG_SIZE = 144;
|
38
60
|
char console_buffer[MAX_CONSOLE_LOG_MSG_SIZE]; // MUST be stack allocated for thread safety
|
@@ -42,32 +64,21 @@ void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *
|
|
42
64
|
this->write_msg_(console_buffer);
|
43
65
|
}
|
44
66
|
|
45
|
-
|
46
|
-
|
47
|
-
if (this->log_callback_.size() > 0) {
|
48
|
-
// This will be processed in the main loop
|
49
|
-
this->log_buffer_->send_message_thread_safe(static_cast<uint8_t>(level), tag, static_cast<uint16_t>(line),
|
50
|
-
current_task, format, args);
|
51
|
-
}
|
52
|
-
#endif // USE_ESPHOME_TASK_LOG_BUFFER
|
53
|
-
|
54
|
-
recursion_guard_.store(false, std::memory_order_release);
|
67
|
+
// Reset the recursion guard for this task
|
68
|
+
this->reset_task_log_recursion_(is_main_task);
|
55
69
|
}
|
56
|
-
#
|
57
|
-
|
58
|
-
#ifndef USE_ESP32
|
59
|
-
// Implementation for platforms that do not support atomic operations
|
60
|
-
// or have to consider logging in other tasks
|
70
|
+
#else
|
71
|
+
// Implementation for all other platforms
|
61
72
|
void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *format, va_list args) { // NOLINT
|
62
|
-
if (level > this->level_for(tag) ||
|
73
|
+
if (level > this->level_for(tag) || global_recursion_guard_)
|
63
74
|
return;
|
64
75
|
|
65
|
-
|
76
|
+
global_recursion_guard_ = true;
|
66
77
|
|
67
78
|
// Format and send to both console and callbacks
|
68
79
|
this->log_message_to_buffer_and_send_(level, tag, line, format, args);
|
69
80
|
|
70
|
-
|
81
|
+
global_recursion_guard_ = false;
|
71
82
|
}
|
72
83
|
#endif // !USE_ESP32
|
73
84
|
|
@@ -76,10 +87,10 @@ void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *
|
|
76
87
|
// Note: USE_STORE_LOG_STR_IN_FLASH is only defined for ESP8266.
|
77
88
|
void Logger::log_vprintf_(int level, const char *tag, int line, const __FlashStringHelper *format,
|
78
89
|
va_list args) { // NOLINT
|
79
|
-
if (level > this->level_for(tag) ||
|
90
|
+
if (level > this->level_for(tag) || global_recursion_guard_)
|
80
91
|
return;
|
81
92
|
|
82
|
-
|
93
|
+
global_recursion_guard_ = true;
|
83
94
|
this->tx_buffer_at_ = 0;
|
84
95
|
|
85
96
|
// Copy format string from progmem
|
@@ -91,7 +102,7 @@ void Logger::log_vprintf_(int level, const char *tag, int line, const __FlashStr
|
|
91
102
|
|
92
103
|
// Buffer full from copying format
|
93
104
|
if (this->tx_buffer_at_ >= this->tx_buffer_size_) {
|
94
|
-
|
105
|
+
global_recursion_guard_ = false; // Make sure to reset the recursion guard before returning
|
95
106
|
return;
|
96
107
|
}
|
97
108
|
|
@@ -107,7 +118,7 @@ void Logger::log_vprintf_(int level, const char *tag, int line, const __FlashStr
|
|
107
118
|
}
|
108
119
|
this->call_log_callbacks_(level, tag, this->tx_buffer_ + msg_start);
|
109
120
|
|
110
|
-
|
121
|
+
global_recursion_guard_ = false;
|
111
122
|
}
|
112
123
|
#endif // USE_STORE_LOG_STR_IN_FLASH
|
113
124
|
|
@@ -179,7 +190,17 @@ void Logger::loop() {
|
|
179
190
|
this->write_footer_to_buffer_(this->tx_buffer_, &this->tx_buffer_at_, this->tx_buffer_size_);
|
180
191
|
this->tx_buffer_[this->tx_buffer_at_] = '\0';
|
181
192
|
this->call_log_callbacks_(message->level, message->tag, this->tx_buffer_);
|
193
|
+
// At this point all the data we need from message has been transferred to the tx_buffer
|
194
|
+
// so we can release the message to allow other tasks to use it as soon as possible.
|
182
195
|
this->log_buffer_->release_message_main_loop(received_token);
|
196
|
+
|
197
|
+
// Write to console from the main loop to prevent corruption from concurrent writes
|
198
|
+
// This ensures all log messages appear on the console in a clean, serialized manner
|
199
|
+
// Note: Messages may appear slightly out of order due to async processing, but
|
200
|
+
// this is preferred over corrupted/interleaved console output
|
201
|
+
if (this->baud_rate_ > 0) {
|
202
|
+
this->write_msg_(this->tx_buffer_);
|
203
|
+
}
|
183
204
|
}
|
184
205
|
}
|
185
206
|
#endif
|
@@ -3,7 +3,7 @@
|
|
3
3
|
#include <cstdarg>
|
4
4
|
#include <map>
|
5
5
|
#ifdef USE_ESP32
|
6
|
-
#include <
|
6
|
+
#include <pthread.h>
|
7
7
|
#endif
|
8
8
|
#include "esphome/core/automation.h"
|
9
9
|
#include "esphome/core/component.h"
|
@@ -84,6 +84,23 @@ enum UARTSelection {
|
|
84
84
|
};
|
85
85
|
#endif // USE_ESP32 || USE_ESP8266 || USE_RP2040 || USE_LIBRETINY
|
86
86
|
|
87
|
+
/**
|
88
|
+
* @brief Logger component for all ESPHome logging.
|
89
|
+
*
|
90
|
+
* This class implements a multi-platform logging system with protection against recursion.
|
91
|
+
*
|
92
|
+
* Recursion Protection Strategy:
|
93
|
+
* - On ESP32: Uses task-specific recursion guards
|
94
|
+
* * Main task: Uses a dedicated boolean member variable for efficiency
|
95
|
+
* * Other tasks: Uses pthread TLS with a dynamically allocated key for task-specific state
|
96
|
+
* - On other platforms: Uses a simple global recursion guard
|
97
|
+
*
|
98
|
+
* We use pthread TLS via pthread_key_create to create a unique key for storing
|
99
|
+
* task-specific recursion state, which:
|
100
|
+
* 1. Efficiently handles multiple tasks without locks or mutexes
|
101
|
+
* 2. Works with ESP-IDF's pthread implementation that uses a linked list for TLS variables
|
102
|
+
* 3. Avoids the limitations of the fixed FreeRTOS task local storage slots
|
103
|
+
*/
|
87
104
|
class Logger : public Component {
|
88
105
|
public:
|
89
106
|
explicit Logger(uint32_t baud_rate, size_t tx_buffer_size);
|
@@ -102,6 +119,9 @@ class Logger : public Component {
|
|
102
119
|
#ifdef USE_ESP_IDF
|
103
120
|
uart_port_t get_uart_num() const { return uart_num_; }
|
104
121
|
#endif
|
122
|
+
#ifdef USE_ESP32
|
123
|
+
void create_pthread_key() { pthread_key_create(&log_recursion_key_, nullptr); }
|
124
|
+
#endif
|
105
125
|
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY)
|
106
126
|
void set_uart_selection(UARTSelection uart_selection) { uart_ = uart_selection; }
|
107
127
|
/// Get the UART used by the logger.
|
@@ -222,18 +242,22 @@ class Logger : public Component {
|
|
222
242
|
std::map<std::string, int> log_levels_{};
|
223
243
|
CallbackManager<void(int, const char *, const char *)> log_callback_{};
|
224
244
|
int current_level_{ESPHOME_LOG_LEVEL_VERY_VERBOSE};
|
225
|
-
#ifdef USE_ESP32
|
226
|
-
std::atomic<bool> recursion_guard_{false};
|
227
245
|
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
|
228
246
|
std::unique_ptr<logger::TaskLogBuffer> log_buffer_; // Will be initialized with init_log_buffer
|
229
247
|
#endif
|
248
|
+
#ifdef USE_ESP32
|
249
|
+
// Task-specific recursion guards:
|
250
|
+
// - Main task uses a dedicated member variable for efficiency
|
251
|
+
// - Other tasks use pthread TLS with a dynamically created key via pthread_key_create
|
252
|
+
bool main_task_recursion_guard_{false};
|
253
|
+
pthread_key_t log_recursion_key_;
|
230
254
|
#else
|
231
|
-
bool
|
255
|
+
bool global_recursion_guard_{false}; // Simple global recursion guard for single-task platforms
|
232
256
|
#endif
|
233
|
-
void *main_task_ = nullptr;
|
234
257
|
CallbackManager<void(int)> level_callback_{};
|
235
258
|
|
236
259
|
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
|
260
|
+
void *main_task_ = nullptr; // Only used for thread name identification
|
237
261
|
const char *HOT get_thread_name_() {
|
238
262
|
TaskHandle_t current_task = xTaskGetCurrentTaskHandle();
|
239
263
|
if (current_task == main_task_) {
|
@@ -248,6 +272,32 @@ class Logger : public Component {
|
|
248
272
|
}
|
249
273
|
#endif
|
250
274
|
|
275
|
+
#ifdef USE_ESP32
|
276
|
+
inline bool HOT check_and_set_task_log_recursion_(bool is_main_task) {
|
277
|
+
if (is_main_task) {
|
278
|
+
const bool was_recursive = main_task_recursion_guard_;
|
279
|
+
main_task_recursion_guard_ = true;
|
280
|
+
return was_recursive;
|
281
|
+
}
|
282
|
+
|
283
|
+
intptr_t current = (intptr_t) pthread_getspecific(log_recursion_key_);
|
284
|
+
if (current != 0)
|
285
|
+
return true;
|
286
|
+
|
287
|
+
pthread_setspecific(log_recursion_key_, (void *) 1);
|
288
|
+
return false;
|
289
|
+
}
|
290
|
+
|
291
|
+
inline void HOT reset_task_log_recursion_(bool is_main_task) {
|
292
|
+
if (is_main_task) {
|
293
|
+
main_task_recursion_guard_ = false;
|
294
|
+
return;
|
295
|
+
}
|
296
|
+
|
297
|
+
pthread_setspecific(log_recursion_key_, (void *) 0);
|
298
|
+
}
|
299
|
+
#endif
|
300
|
+
|
251
301
|
inline void HOT write_header_to_buffer_(int level, const char *tag, int line, const char *thread_name, char *buffer,
|
252
302
|
int *buffer_at, int buffer_size) {
|
253
303
|
// Format header
|