esphome 2024.10.3__py3-none-any.whl → 2024.11.0__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 +22 -4
- esphome/automation.py +29 -2
- esphome/components/animation/__init__.py +5 -8
- esphome/components/animation/animation.cpp +1 -1
- esphome/components/audio/__init__.py +9 -0
- esphome/components/audio/audio.h +21 -0
- esphome/components/axs15231/__init__.py +6 -0
- esphome/components/axs15231/touchscreen/__init__.py +36 -0
- esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp +64 -0
- esphome/components/axs15231/touchscreen/axs15231_touchscreen.h +27 -0
- esphome/components/bme68x_bsec2/__init__.py +1 -1
- esphome/components/bme68x_bsec2/bme68x_bsec2.cpp +50 -47
- esphome/components/bme68x_bsec2/bme68x_bsec2.h +0 -2
- esphome/components/bytebuffer/__init__.py +5 -0
- esphome/components/bytebuffer/bytebuffer.h +421 -0
- esphome/components/climate/__init__.py +14 -13
- esphome/components/datetime/__init__.py +3 -3
- esphome/components/debug/debug_esp32.cpp +16 -8
- esphome/components/dfplayer/dfplayer.cpp +132 -6
- esphome/components/dfplayer/dfplayer.h +19 -53
- esphome/components/display/display.cpp +142 -0
- esphome/components/display/display.h +7 -0
- esphome/components/es8311/__init__.py +0 -0
- esphome/components/es8311/audio_dac.py +70 -0
- esphome/components/es8311/es8311.cpp +227 -0
- esphome/components/es8311/es8311.h +135 -0
- esphome/components/es8311/es8311_const.h +195 -0
- esphome/components/esp32/boards.py +199 -1
- esphome/components/esp32/gpio.py +3 -1
- esphome/components/esp32_ble/const_esp32c6.h +7 -0
- esphome/components/esp32_ble_client/ble_client_base.h +1 -1
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +3 -0
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +2 -1
- esphome/components/esp32_rmt_led_strip/led_strip.cpp +2 -2
- esphome/components/esp32_rmt_led_strip/led_strip.h +2 -0
- esphome/components/esp32_rmt_led_strip/light.py +3 -1
- esphome/components/esp8266/gpio.py +7 -5
- esphome/components/ethernet/__init__.py +55 -1
- esphome/components/ethernet/ethernet_component.cpp +14 -1
- esphome/components/ethernet/ethernet_component.h +7 -1
- esphome/components/font/__init__.py +213 -108
- esphome/components/gp8403/output/__init__.py +1 -1
- esphome/components/host/gpio.py +6 -4
- esphome/components/http_request/__init__.py +12 -0
- esphome/components/http_request/http_request.h +65 -3
- esphome/components/http_request/http_request_arduino.cpp +4 -3
- esphome/components/http_request/http_request_idf.cpp +12 -14
- esphome/components/http_request/ota/ota_http_request.cpp +1 -1
- esphome/components/http_request/update/http_request_update.cpp +1 -1
- esphome/components/i2c_device/__init__.py +26 -0
- esphome/components/i2c_device/i2c_device.cpp +17 -0
- esphome/components/i2c_device/i2c_device.h +18 -0
- esphome/components/i2s_audio/__init__.py +1 -3
- esphome/components/i2s_audio/speaker/__init__.py +12 -4
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +426 -200
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +92 -33
- esphome/components/ili9xxx/display.py +5 -1
- esphome/components/image/__init__.py +5 -8
- esphome/components/image/image.cpp +14 -14
- esphome/components/image/image.h +20 -24
- esphome/components/internal_temperature/internal_temperature.cpp +51 -2
- esphome/components/internal_temperature/internal_temperature.h +1 -0
- esphome/components/ld2420/ld2420.cpp +1 -1
- esphome/components/libretiny/gpio.py +4 -2
- esphome/components/light/__init__.py +32 -1
- esphome/components/light/automation.py +39 -32
- esphome/components/light/effects.py +36 -36
- esphome/components/light/light_state.cpp +6 -16
- esphome/components/light/light_state.h +34 -0
- esphome/components/light/types.py +3 -1
- esphome/components/logger/logger_esp32.cpp +15 -0
- esphome/components/lvgl/__init__.py +202 -95
- esphome/components/lvgl/automation.py +42 -40
- esphome/components/lvgl/binary_sensor/__init__.py +8 -15
- esphome/components/lvgl/defines.py +14 -8
- esphome/components/lvgl/encoders.py +11 -8
- esphome/components/lvgl/keypads.py +77 -0
- esphome/components/lvgl/light/__init__.py +6 -8
- esphome/components/lvgl/lv_validation.py +2 -4
- esphome/components/lvgl/lvcode.py +3 -9
- esphome/components/lvgl/lvgl_esphome.cpp +210 -89
- esphome/components/lvgl/lvgl_esphome.h +113 -30
- esphome/components/lvgl/lvgl_proxy.h +17 -0
- esphome/components/lvgl/number/__init__.py +10 -15
- esphome/components/lvgl/schemas.py +4 -2
- esphome/components/lvgl/select/__init__.py +12 -37
- esphome/components/lvgl/select/lvgl_select.h +27 -33
- esphome/components/lvgl/sensor/__init__.py +8 -14
- esphome/components/lvgl/styles.py +3 -4
- esphome/components/lvgl/switch/__init__.py +8 -13
- esphome/components/lvgl/text/__init__.py +5 -6
- esphome/components/lvgl/text_sensor/__init__.py +15 -15
- esphome/components/lvgl/touchscreens.py +2 -3
- esphome/components/lvgl/trigger.py +7 -9
- esphome/components/lvgl/types.py +9 -3
- esphome/components/lvgl/widgets/__init__.py +32 -21
- esphome/components/lvgl/widgets/dropdown.py +22 -10
- esphome/components/lvgl/widgets/msgbox.py +6 -5
- esphome/components/lvgl/widgets/obj.py +4 -2
- esphome/components/lvgl/widgets/page.py +3 -2
- esphome/components/lvgl/widgets/qrcode.py +54 -0
- esphome/components/lvgl/widgets/roller.py +21 -14
- esphome/components/lvgl/widgets/tileview.py +2 -1
- esphome/components/max17043/__init__.py +1 -0
- esphome/components/max17043/automation.h +20 -0
- esphome/components/max17043/max17043.cpp +98 -0
- esphome/components/max17043/max17043.h +29 -0
- esphome/components/max17043/sensor.py +77 -0
- esphome/components/media_player/__init__.py +11 -0
- esphome/components/media_player/automation.h +10 -0
- esphome/components/media_player/media_player.cpp +4 -0
- esphome/components/midea/air_conditioner.cpp +17 -1
- esphome/components/mlx90393/sensor.py +1 -1
- esphome/components/modbus_controller/__init__.py +31 -1
- esphome/components/modbus_controller/automation.h +16 -0
- esphome/components/modbus_controller/const.py +2 -0
- esphome/components/modbus_controller/modbus_controller.cpp +14 -2
- esphome/components/modbus_controller/modbus_controller.h +9 -0
- esphome/components/mopeka_pro_check/mopeka_pro_check.cpp +40 -21
- esphome/components/mopeka_pro_check/mopeka_pro_check.h +9 -2
- esphome/components/mopeka_pro_check/sensor.py +41 -0
- esphome/components/mqtt/__init__.py +36 -0
- esphome/components/mqtt/mqtt_client.cpp +27 -3
- esphome/components/mqtt/mqtt_client.h +27 -2
- esphome/components/mqtt/mqtt_climate.cpp +4 -2
- esphome/components/mqtt/mqtt_component.cpp +6 -0
- esphome/components/mqtt/mqtt_component.h +4 -0
- esphome/components/mqtt/mqtt_const.h +6 -0
- esphome/components/online_image/online_image.cpp +2 -8
- esphome/components/online_image/online_image.h +2 -6
- esphome/components/opentherm/__init__.py +35 -9
- esphome/components/opentherm/binary_sensor/__init__.py +33 -0
- esphome/components/opentherm/const.py +11 -0
- esphome/components/opentherm/generate.py +142 -0
- esphome/components/opentherm/hub.cpp +130 -24
- esphome/components/opentherm/hub.h +62 -9
- esphome/components/opentherm/input.h +18 -0
- esphome/components/opentherm/input.py +51 -0
- esphome/components/opentherm/number/__init__.py +74 -0
- esphome/components/opentherm/number/number.cpp +40 -0
- esphome/components/opentherm/number/number.h +31 -0
- esphome/components/opentherm/opentherm.cpp +30 -0
- esphome/components/opentherm/opentherm.h +34 -2
- esphome/components/opentherm/opentherm_macros.h +151 -0
- esphome/components/opentherm/output/__init__.py +47 -0
- esphome/components/opentherm/output/output.cpp +18 -0
- esphome/components/opentherm/output/output.h +33 -0
- esphome/components/opentherm/schema.py +814 -0
- esphome/components/opentherm/sensor/__init__.py +51 -0
- esphome/components/opentherm/switch/__init__.py +43 -0
- esphome/components/opentherm/switch/switch.cpp +28 -0
- esphome/components/opentherm/switch/switch.h +20 -0
- esphome/components/opentherm/validate.py +31 -0
- esphome/components/pcd8544/display.py +8 -4
- esphome/components/prometheus/prometheus_handler.cpp +176 -14
- esphome/components/prometheus/prometheus_handler.h +25 -7
- esphome/components/qspi_amoled/display.py +1 -141
- esphome/components/qspi_dbi/display.py +185 -0
- esphome/components/qspi_dbi/models.py +64 -0
- esphome/components/{qspi_amoled/qspi_amoled.cpp → qspi_dbi/qspi_dbi.cpp} +95 -46
- esphome/components/{qspi_amoled/qspi_amoled.h → qspi_dbi/qspi_dbi.h} +26 -15
- esphome/components/rp2040/__init__.py +6 -3
- esphome/components/rp2040/gpio.py +5 -3
- esphome/components/rtttl/rtttl.cpp +4 -1
- esphome/components/rtttl/rtttl.h +1 -0
- esphome/components/sdl/sdl_esphome.cpp +22 -5
- esphome/components/sdl/sdl_esphome.h +1 -0
- esphome/components/sdm_meter/sdm_meter.cpp +1 -1
- esphome/components/sensor/__init__.py +18 -8
- esphome/components/sensor/filter.cpp +19 -18
- esphome/components/sensor/filter.h +9 -10
- esphome/components/sgp4x/sgp4x.cpp +40 -74
- esphome/components/sgp4x/sgp4x.h +5 -3
- esphome/components/speaker/__init__.py +51 -5
- esphome/components/speaker/automation.h +25 -0
- esphome/components/speaker/speaker.h +72 -1
- esphome/components/spi/__init__.py +15 -14
- esphome/components/spi_device/__init__.py +4 -15
- esphome/components/ssd1306_spi/display.py +6 -2
- esphome/components/ssd1322_spi/display.py +6 -2
- esphome/components/ssd1325_spi/display.py +6 -2
- esphome/components/ssd1327_spi/display.py +6 -2
- esphome/components/ssd1331_spi/display.py +6 -2
- esphome/components/ssd1351_spi/display.py +6 -2
- esphome/components/st7567_spi/display.py +6 -2
- esphome/components/st7701s/display.py +5 -1
- esphome/components/st7735/display.py +10 -5
- esphome/components/st7789v/display.py +12 -7
- esphome/components/statsd/statsd.cpp +2 -0
- esphome/components/statsd/statsd.h +2 -0
- esphome/components/sun/sun.h +3 -0
- esphome/components/tc74/__init__.py +1 -0
- esphome/components/tc74/sensor.py +32 -0
- esphome/components/tc74/tc74.cpp +68 -0
- esphome/components/tc74/tc74.h +28 -0
- esphome/components/touchscreen/__init__.py +41 -50
- esphome/components/touchscreen/touchscreen.h +4 -8
- esphome/components/tuya/fan/tuya_fan.cpp +1 -1
- esphome/components/udp/udp_component.cpp +6 -3
- esphome/components/udp/udp_component.h +4 -2
- esphome/components/waveshare_epaper/display.py +6 -2
- esphome/components/web_server/web_server.cpp +22 -0
- esphome/components/web_server/web_server.h +3 -0
- esphome/components/weikai/weikai.h +2 -2
- esphome/components/wifi/wifi_component.cpp +2 -2
- esphome/components/wifi/wifi_component_esp32_arduino.cpp +4 -4
- esphome/components/wifi/wifi_component_esp8266.cpp +4 -4
- esphome/components/wifi/wifi_component_esp_idf.cpp +2 -2
- esphome/components/xpt2046/touchscreen/__init__.py +7 -32
- esphome/config_validation.py +3 -1
- esphome/const.py +9 -2
- esphome/core/defines.h +8 -2
- esphome/core/helpers.cpp +32 -17
- esphome/core/helpers.h +32 -16
- esphome/core/ring_buffer.cpp +2 -2
- esphome/core/ring_buffer.h +2 -2
- esphome/dashboard/core.py +25 -0
- esphome/dashboard/status/mdns.py +3 -4
- esphome/dashboard/web_server.py +54 -19
- esphome/espota2.py +36 -35
- esphome/helpers.py +68 -16
- esphome/mqtt.py +9 -2
- esphome/storage_json.py +4 -0
- esphome/writer.py +7 -18
- esphome/zeroconf.py +8 -6
- {esphome-2024.10.3.dist-info → esphome-2024.11.0.dist-info}/METADATA +7 -5
- {esphome-2024.10.3.dist-info → esphome-2024.11.0.dist-info}/RECORD +232 -186
- esphome/core/bytebuffer.cpp +0 -167
- esphome/core/bytebuffer.h +0 -144
- /esphome/components/{qspi_amoled → qspi_dbi}/__init__.py +0 -0
- {esphome-2024.10.3.dist-info → esphome-2024.11.0.dist-info}/LICENSE +0 -0
- {esphome-2024.10.3.dist-info → esphome-2024.11.0.dist-info}/WHEEL +0 -0
- {esphome-2024.10.3.dist-info → esphome-2024.11.0.dist-info}/entry_points.txt +0 -0
- {esphome-2024.10.3.dist-info → esphome-2024.11.0.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,3 @@
|
|
1
|
-
import esphome.codegen as cg
|
2
1
|
import esphome.config_validation as cv
|
3
2
|
from esphome.const import CONF_OPTIONS
|
4
3
|
|
@@ -9,21 +8,24 @@ from ..defines import (
|
|
9
8
|
CONF_SCROLLBAR,
|
10
9
|
CONF_SELECTED,
|
11
10
|
CONF_SELECTED_INDEX,
|
11
|
+
CONF_SELECTED_TEXT,
|
12
12
|
CONF_SYMBOL,
|
13
13
|
DIRECTIONS,
|
14
14
|
literal,
|
15
15
|
)
|
16
|
+
from ..helpers import lvgl_components_required
|
16
17
|
from ..lv_validation import lv_int, lv_text, option_string
|
17
|
-
from ..lvcode import LocalVariable, lv, lv_expr
|
18
|
+
from ..lvcode import LocalVariable, lv, lv_add, lv_expr
|
18
19
|
from ..schemas import part_schema
|
19
|
-
from ..types import LvSelect, LvType, lv_obj_t
|
20
|
+
from ..types import LvCompound, LvSelect, LvType, lv_obj_t
|
20
21
|
from . import Widget, WidgetType, set_obj_properties
|
21
22
|
from .label import CONF_LABEL
|
22
23
|
|
23
24
|
CONF_DROPDOWN = "dropdown"
|
24
25
|
CONF_DROPDOWN_LIST = "dropdown_list"
|
25
26
|
|
26
|
-
lv_dropdown_t = LvSelect("
|
27
|
+
lv_dropdown_t = LvSelect("LvDropdownType", parents=(LvCompound,))
|
28
|
+
|
27
29
|
lv_dropdown_list_t = LvType("lv_dropdown_list_t")
|
28
30
|
dropdown_list_spec = WidgetType(
|
29
31
|
CONF_DROPDOWN_LIST, lv_dropdown_list_t, (CONF_MAIN, CONF_SELECTED, CONF_SCROLLBAR)
|
@@ -32,7 +34,8 @@ dropdown_list_spec = WidgetType(
|
|
32
34
|
DROPDOWN_BASE_SCHEMA = cv.Schema(
|
33
35
|
{
|
34
36
|
cv.Optional(CONF_SYMBOL): lv_text,
|
35
|
-
cv.
|
37
|
+
cv.Exclusive(CONF_SELECTED_INDEX, CONF_SELECTED_TEXT): lv_int,
|
38
|
+
cv.Exclusive(CONF_SELECTED_TEXT, CONF_SELECTED_TEXT): lv_text,
|
36
39
|
cv.Optional(CONF_DIR, default="BOTTOM"): DIRECTIONS.one_of,
|
37
40
|
cv.Optional(CONF_DROPDOWN_LIST): part_schema(dropdown_list_spec),
|
38
41
|
}
|
@@ -44,6 +47,12 @@ DROPDOWN_SCHEMA = DROPDOWN_BASE_SCHEMA.extend(
|
|
44
47
|
}
|
45
48
|
)
|
46
49
|
|
50
|
+
DROPDOWN_UPDATE_SCHEMA = DROPDOWN_BASE_SCHEMA.extend(
|
51
|
+
{
|
52
|
+
cv.Optional(CONF_OPTIONS): cv.ensure_list(option_string),
|
53
|
+
}
|
54
|
+
)
|
55
|
+
|
47
56
|
|
48
57
|
class DropdownType(WidgetType):
|
49
58
|
def __init__(self):
|
@@ -52,18 +61,21 @@ class DropdownType(WidgetType):
|
|
52
61
|
lv_dropdown_t,
|
53
62
|
(CONF_MAIN, CONF_INDICATOR),
|
54
63
|
DROPDOWN_SCHEMA,
|
55
|
-
|
64
|
+
modify_schema=DROPDOWN_UPDATE_SCHEMA,
|
56
65
|
)
|
57
66
|
|
58
67
|
async def to_code(self, w: Widget, config):
|
68
|
+
lvgl_components_required.add(CONF_DROPDOWN)
|
59
69
|
if options := config.get(CONF_OPTIONS):
|
60
|
-
|
61
|
-
lv.dropdown_set_options(w.obj, text)
|
70
|
+
lv_add(w.var.set_options(options))
|
62
71
|
if symbol := config.get(CONF_SYMBOL):
|
63
|
-
lv.dropdown_set_symbol(w.obj, await lv_text.process(symbol))
|
72
|
+
lv.dropdown_set_symbol(w.var.obj, await lv_text.process(symbol))
|
64
73
|
if (selected := config.get(CONF_SELECTED_INDEX)) is not None:
|
65
74
|
value = await lv_int.process(selected)
|
66
|
-
|
75
|
+
lv_add(w.var.set_selected_index(value, literal("LV_ANIM_OFF")))
|
76
|
+
if (selected := config.get(CONF_SELECTED_TEXT)) is not None:
|
77
|
+
value = await lv_text.process(selected)
|
78
|
+
lv_add(w.var.set_selected_text(value, literal("LV_ANIM_OFF")))
|
67
79
|
if dirn := config.get(CONF_DIR):
|
68
80
|
lv.dropdown_set_dir(w.obj, literal(dirn))
|
69
81
|
if dlist := config.get(CONF_DROPDOWN_LIST):
|
@@ -20,6 +20,7 @@ from ..lvcode import (
|
|
20
20
|
EVENT_ARG,
|
21
21
|
LambdaContext,
|
22
22
|
LocalVariable,
|
23
|
+
lv,
|
23
24
|
lv_add,
|
24
25
|
lv_assign,
|
25
26
|
lv_expr,
|
@@ -27,7 +28,6 @@ from ..lvcode import (
|
|
27
28
|
lv_Pvariable,
|
28
29
|
)
|
29
30
|
from ..schemas import STYLE_SCHEMA, STYLED_TEXT_SCHEMA, container_schema, part_schema
|
30
|
-
from ..styles import TOP_LAYER
|
31
31
|
from ..types import LV_EVENT, char_ptr, lv_obj_t
|
32
32
|
from . import Widget, set_obj_properties
|
33
33
|
from .button import button_spec
|
@@ -59,7 +59,7 @@ MSGBOX_SCHEMA = container_schema(
|
|
59
59
|
)
|
60
60
|
|
61
61
|
|
62
|
-
async def msgbox_to_code(conf):
|
62
|
+
async def msgbox_to_code(top_layer, conf):
|
63
63
|
"""
|
64
64
|
Construct a message box. This consists of a full-screen translucent background enclosing a centered container
|
65
65
|
with an optional title, body, close button and a button matrix. And any other widgets the user cares to add
|
@@ -101,7 +101,7 @@ async def msgbox_to_code(conf):
|
|
101
101
|
text = await lv_text.process(conf[CONF_BODY].get(CONF_TEXT, ""))
|
102
102
|
title = await lv_text.process(conf[CONF_TITLE].get(CONF_TEXT, ""))
|
103
103
|
close_button = conf[CONF_CLOSE_BUTTON]
|
104
|
-
lv_assign(outer, lv_expr.obj_create(
|
104
|
+
lv_assign(outer, lv_expr.obj_create(top_layer))
|
105
105
|
lv_obj.set_width(outer, lv_pct(100))
|
106
106
|
lv_obj.set_height(outer, lv_pct(100))
|
107
107
|
lv_obj.set_style_bg_opa(outer, 128, 0)
|
@@ -141,6 +141,7 @@ async def msgbox_to_code(conf):
|
|
141
141
|
set_btn_data(buttonmatrix.obj, ctrl_list, width_list)
|
142
142
|
|
143
143
|
|
144
|
-
async def msgboxes_to_code(config):
|
144
|
+
async def msgboxes_to_code(lv_component, config):
|
145
|
+
top_layer = lv.disp_get_layer_top(lv_component.get_disp())
|
145
146
|
for conf in config.get(CONF_MSGBOXES, ()):
|
146
|
-
await msgbox_to_code(conf)
|
147
|
+
await msgbox_to_code(top_layer, conf)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from esphome import automation
|
2
2
|
|
3
3
|
from ..automation import update_to_code
|
4
|
-
from ..defines import CONF_MAIN, CONF_OBJ
|
4
|
+
from ..defines import CONF_MAIN, CONF_OBJ, CONF_SCROLLBAR
|
5
5
|
from ..schemas import create_modify_schema
|
6
6
|
from ..types import ObjUpdateAction, WidgetType, lv_obj_t
|
7
7
|
|
@@ -12,7 +12,9 @@ class ObjType(WidgetType):
|
|
12
12
|
"""
|
13
13
|
|
14
14
|
def __init__(self):
|
15
|
-
super().__init__(
|
15
|
+
super().__init__(
|
16
|
+
CONF_OBJ, lv_obj_t, (CONF_MAIN, CONF_SCROLLBAR), schema={}, modify_schema={}
|
17
|
+
)
|
16
18
|
|
17
19
|
async def to_code(self, w, config):
|
18
20
|
return []
|
@@ -20,6 +20,7 @@ from ..lvcode import (
|
|
20
20
|
add_line_marks,
|
21
21
|
lv_add,
|
22
22
|
lvgl_comp,
|
23
|
+
lvgl_static,
|
23
24
|
)
|
24
25
|
from ..schemas import LVGL_SCHEMA
|
25
26
|
from ..types import LvglAction, lv_page_t
|
@@ -139,7 +140,7 @@ async def add_pages(lv_component, config):
|
|
139
140
|
await add_widgets(page, pconf)
|
140
141
|
|
141
142
|
|
142
|
-
async def generate_page_triggers(
|
143
|
+
async def generate_page_triggers(config):
|
143
144
|
for pconf in config.get(CONF_PAGES, ()):
|
144
145
|
page = (await get_widgets(pconf))[0]
|
145
146
|
for ev in (CONF_ON_LOAD, CONF_ON_UNLOAD):
|
@@ -149,7 +150,7 @@ async def generate_page_triggers(lv_component, config):
|
|
149
150
|
async with LambdaContext(EVENT_ARG, where=id) as context:
|
150
151
|
lv_add(trigger.trigger())
|
151
152
|
lv_add(
|
152
|
-
|
153
|
+
lvgl_static.add_event_cb(
|
153
154
|
page.obj,
|
154
155
|
await context.get_lambda(),
|
155
156
|
literal(f"LV_EVENT_SCREEN_{ev[3:].upper()}_START"),
|
@@ -0,0 +1,54 @@
|
|
1
|
+
import esphome.codegen as cg
|
2
|
+
import esphome.config_validation as cv
|
3
|
+
from esphome.const import CONF_SIZE, CONF_TEXT
|
4
|
+
from esphome.cpp_generator import MockObjClass
|
5
|
+
|
6
|
+
from ..defines import CONF_MAIN, literal
|
7
|
+
from ..lv_validation import color, color_retmapper, lv_text
|
8
|
+
from ..lvcode import LocalVariable, lv, lv_expr
|
9
|
+
from ..schemas import TEXT_SCHEMA
|
10
|
+
from ..types import WidgetType, lv_obj_t
|
11
|
+
from . import Widget
|
12
|
+
|
13
|
+
CONF_QRCODE = "qrcode"
|
14
|
+
CONF_DARK_COLOR = "dark_color"
|
15
|
+
CONF_LIGHT_COLOR = "light_color"
|
16
|
+
|
17
|
+
QRCODE_SCHEMA = TEXT_SCHEMA.extend(
|
18
|
+
{
|
19
|
+
cv.Optional(CONF_DARK_COLOR, default="black"): color,
|
20
|
+
cv.Optional(CONF_LIGHT_COLOR, default="white"): color,
|
21
|
+
cv.Required(CONF_SIZE): cv.int_,
|
22
|
+
}
|
23
|
+
)
|
24
|
+
|
25
|
+
|
26
|
+
class QrCodeType(WidgetType):
|
27
|
+
def __init__(self):
|
28
|
+
super().__init__(
|
29
|
+
CONF_QRCODE,
|
30
|
+
lv_obj_t,
|
31
|
+
(CONF_MAIN,),
|
32
|
+
QRCODE_SCHEMA,
|
33
|
+
modify_schema=TEXT_SCHEMA,
|
34
|
+
)
|
35
|
+
|
36
|
+
def get_uses(self):
|
37
|
+
return ("canvas", "img")
|
38
|
+
|
39
|
+
def obj_creator(self, parent: MockObjClass, config: dict):
|
40
|
+
dark_color = color_retmapper(config[CONF_DARK_COLOR])
|
41
|
+
light_color = color_retmapper(config[CONF_LIGHT_COLOR])
|
42
|
+
size = config[CONF_SIZE]
|
43
|
+
return lv_expr.call("qrcode_create", parent, size, dark_color, light_color)
|
44
|
+
|
45
|
+
async def to_code(self, w: Widget, config):
|
46
|
+
if (value := config.get(CONF_TEXT)) is not None:
|
47
|
+
value = await lv_text.process(value)
|
48
|
+
with LocalVariable(
|
49
|
+
"qr_text", cg.const_char_ptr, value, modifier=""
|
50
|
+
) as str_obj:
|
51
|
+
lv.qrcode_update(w.obj, str_obj, literal(f"strlen({str_obj})"))
|
52
|
+
|
53
|
+
|
54
|
+
qr_code_spec = QrCodeType()
|
@@ -1,4 +1,3 @@
|
|
1
|
-
import esphome.codegen as cg
|
2
1
|
import esphome.config_validation as cv
|
3
2
|
from esphome.const import CONF_MODE, CONF_OPTIONS
|
4
3
|
|
@@ -7,36 +6,40 @@ from ..defines import (
|
|
7
6
|
CONF_MAIN,
|
8
7
|
CONF_SELECTED,
|
9
8
|
CONF_SELECTED_INDEX,
|
9
|
+
CONF_SELECTED_TEXT,
|
10
10
|
CONF_VISIBLE_ROW_COUNT,
|
11
11
|
ROLLER_MODES,
|
12
12
|
literal,
|
13
13
|
)
|
14
|
-
from ..
|
15
|
-
from ..
|
14
|
+
from ..helpers import lvgl_components_required
|
15
|
+
from ..lv_validation import animated, lv_int, lv_text, option_string
|
16
|
+
from ..lvcode import lv_add
|
16
17
|
from ..types import LvSelect
|
17
18
|
from . import WidgetType
|
18
19
|
from .label import CONF_LABEL
|
19
20
|
|
20
21
|
CONF_ROLLER = "roller"
|
21
|
-
lv_roller_t = LvSelect("
|
22
|
+
lv_roller_t = LvSelect("LvRollerType")
|
22
23
|
|
23
24
|
ROLLER_BASE_SCHEMA = cv.Schema(
|
24
25
|
{
|
25
|
-
cv.
|
26
|
+
cv.Exclusive(CONF_SELECTED_INDEX, CONF_SELECTED_TEXT): lv_int,
|
27
|
+
cv.Exclusive(CONF_SELECTED_TEXT, CONF_SELECTED_TEXT): lv_text,
|
26
28
|
cv.Optional(CONF_VISIBLE_ROW_COUNT): lv_int,
|
29
|
+
cv.Optional(CONF_MODE): ROLLER_MODES.one_of,
|
27
30
|
}
|
28
31
|
)
|
29
32
|
|
30
33
|
ROLLER_SCHEMA = ROLLER_BASE_SCHEMA.extend(
|
31
34
|
{
|
32
35
|
cv.Required(CONF_OPTIONS): cv.ensure_list(option_string),
|
33
|
-
cv.Optional(CONF_MODE, default="NORMAL"): ROLLER_MODES.one_of,
|
34
36
|
}
|
35
37
|
)
|
36
38
|
|
37
39
|
ROLLER_MODIFY_SCHEMA = ROLLER_BASE_SCHEMA.extend(
|
38
40
|
{
|
39
41
|
cv.Optional(CONF_ANIMATED, default=True): animated,
|
42
|
+
cv.Optional(CONF_OPTIONS): cv.ensure_list(option_string),
|
40
43
|
}
|
41
44
|
)
|
42
45
|
|
@@ -52,15 +55,19 @@ class RollerType(WidgetType):
|
|
52
55
|
)
|
53
56
|
|
54
57
|
async def to_code(self, w, config):
|
58
|
+
lvgl_components_required.add(CONF_ROLLER)
|
59
|
+
if mode := config.get(CONF_MODE):
|
60
|
+
mode = await ROLLER_MODES.process(mode)
|
61
|
+
lv_add(w.var.set_mode(mode))
|
55
62
|
if options := config.get(CONF_OPTIONS):
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
63
|
+
lv_add(w.var.set_options(options))
|
64
|
+
animopt = literal(config.get(CONF_ANIMATED, "LV_ANIM_OFF"))
|
65
|
+
if (selected := config.get(CONF_SELECTED_INDEX)) is not None:
|
66
|
+
value = await lv_int.process(selected)
|
67
|
+
lv_add(w.var.set_selected_index(value, animopt))
|
68
|
+
if (selected := config.get(CONF_SELECTED_TEXT)) is not None:
|
69
|
+
value = await lv_text.process(selected)
|
70
|
+
lv_add(w.var.set_selected_text(value, animopt))
|
64
71
|
await w.set_property(
|
65
72
|
CONF_VISIBLE_ROW_COUNT,
|
66
73
|
await lv_int.process(config.get(CONF_VISIBLE_ROW_COUNT)),
|
@@ -9,6 +9,7 @@ from ..defines import (
|
|
9
9
|
CONF_COLUMN,
|
10
10
|
CONF_DIR,
|
11
11
|
CONF_MAIN,
|
12
|
+
CONF_SCROLLBAR,
|
12
13
|
CONF_TILE_ID,
|
13
14
|
CONF_TILES,
|
14
15
|
TILE_DIRECTIONS,
|
@@ -56,7 +57,7 @@ class TileviewType(WidgetType):
|
|
56
57
|
super().__init__(
|
57
58
|
CONF_TILEVIEW,
|
58
59
|
lv_tileview_t,
|
59
|
-
(CONF_MAIN,),
|
60
|
+
(CONF_MAIN, CONF_SCROLLBAR),
|
60
61
|
schema=TILEVIEW_SCHEMA,
|
61
62
|
modify_schema={},
|
62
63
|
)
|
@@ -0,0 +1 @@
|
|
1
|
+
CODEOWNERS = ["@blacknell"]
|
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
#pragma once
|
3
|
+
#include "esphome/core/automation.h"
|
4
|
+
#include "max17043.h"
|
5
|
+
|
6
|
+
namespace esphome {
|
7
|
+
namespace max17043 {
|
8
|
+
|
9
|
+
template<typename... Ts> class SleepAction : public Action<Ts...> {
|
10
|
+
public:
|
11
|
+
explicit SleepAction(MAX17043Component *max17043) : max17043_(max17043) {}
|
12
|
+
|
13
|
+
void play(Ts... x) override { this->max17043_->sleep_mode(); }
|
14
|
+
|
15
|
+
protected:
|
16
|
+
MAX17043Component *max17043_;
|
17
|
+
};
|
18
|
+
|
19
|
+
} // namespace max17043
|
20
|
+
} // namespace esphome
|
@@ -0,0 +1,98 @@
|
|
1
|
+
#include "max17043.h"
|
2
|
+
#include "esphome/core/log.h"
|
3
|
+
|
4
|
+
namespace esphome {
|
5
|
+
namespace max17043 {
|
6
|
+
|
7
|
+
// MAX174043 is a 1-Cell Fuel Gauge with ModelGauge and Low-Battery Alert
|
8
|
+
// Consult the datasheet at https://www.analog.com/en/products/max17043.html
|
9
|
+
|
10
|
+
static const char *const TAG = "max17043";
|
11
|
+
|
12
|
+
static const uint8_t MAX17043_VCELL = 0x02;
|
13
|
+
static const uint8_t MAX17043_SOC = 0x04;
|
14
|
+
static const uint8_t MAX17043_CONFIG = 0x0c;
|
15
|
+
|
16
|
+
static const uint16_t MAX17043_CONFIG_POWER_UP_DEFAULT = 0x971C;
|
17
|
+
static const uint16_t MAX17043_CONFIG_SAFE_MASK = 0xFF1F; // mask out sleep bit (7), unused bit (6) and alert bit (4)
|
18
|
+
static const uint16_t MAX17043_CONFIG_SLEEP_MASK = 0x0080;
|
19
|
+
|
20
|
+
void MAX17043Component::update() {
|
21
|
+
uint16_t raw_voltage, raw_percent;
|
22
|
+
|
23
|
+
if (this->voltage_sensor_ != nullptr) {
|
24
|
+
if (!this->read_byte_16(MAX17043_VCELL, &raw_voltage)) {
|
25
|
+
this->status_set_warning("Unable to read MAX17043_VCELL");
|
26
|
+
} else {
|
27
|
+
float voltage = (1.25 * (float) (raw_voltage >> 4)) / 1000.0;
|
28
|
+
this->voltage_sensor_->publish_state(voltage);
|
29
|
+
this->status_clear_warning();
|
30
|
+
}
|
31
|
+
}
|
32
|
+
if (this->battery_remaining_sensor_ != nullptr) {
|
33
|
+
if (!this->read_byte_16(MAX17043_SOC, &raw_percent)) {
|
34
|
+
this->status_set_warning("Unable to read MAX17043_SOC");
|
35
|
+
} else {
|
36
|
+
float percent = (float) ((raw_percent >> 8) + 0.003906f * (raw_percent & 0x00ff));
|
37
|
+
this->battery_remaining_sensor_->publish_state(percent);
|
38
|
+
this->status_clear_warning();
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
void MAX17043Component::setup() {
|
44
|
+
ESP_LOGCONFIG(TAG, "Setting up MAX17043...");
|
45
|
+
|
46
|
+
uint16_t config_reg;
|
47
|
+
if (this->write(&MAX17043_CONFIG, 1) != i2c::ERROR_OK) {
|
48
|
+
this->status_set_warning();
|
49
|
+
return;
|
50
|
+
}
|
51
|
+
|
52
|
+
if (this->read(reinterpret_cast<uint8_t *>(&config_reg), 2) != i2c::ERROR_OK) {
|
53
|
+
this->status_set_warning();
|
54
|
+
return;
|
55
|
+
}
|
56
|
+
|
57
|
+
config_reg = i2c::i2ctohs(config_reg) & MAX17043_CONFIG_SAFE_MASK;
|
58
|
+
ESP_LOGV(TAG, "MAX17043 CONFIG register reads 0x%X", config_reg);
|
59
|
+
|
60
|
+
if (config_reg != MAX17043_CONFIG_POWER_UP_DEFAULT) {
|
61
|
+
ESP_LOGE(TAG, "Device does not appear to be a MAX17043");
|
62
|
+
this->status_set_error("unrecognised");
|
63
|
+
this->mark_failed();
|
64
|
+
return;
|
65
|
+
}
|
66
|
+
|
67
|
+
// need to write back to config register to reset the sleep bit
|
68
|
+
if (!this->write_byte_16(MAX17043_CONFIG, MAX17043_CONFIG_POWER_UP_DEFAULT)) {
|
69
|
+
this->status_set_error("sleep reset failed");
|
70
|
+
this->mark_failed();
|
71
|
+
return;
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
void MAX17043Component::dump_config() {
|
76
|
+
ESP_LOGCONFIG(TAG, "MAX17043:");
|
77
|
+
LOG_I2C_DEVICE(this);
|
78
|
+
if (this->is_failed()) {
|
79
|
+
ESP_LOGE(TAG, "Communication with MAX17043 failed");
|
80
|
+
}
|
81
|
+
LOG_UPDATE_INTERVAL(this);
|
82
|
+
LOG_SENSOR(" ", "Battery Voltage", this->voltage_sensor_);
|
83
|
+
LOG_SENSOR(" ", "Battery Level", this->battery_remaining_sensor_);
|
84
|
+
}
|
85
|
+
|
86
|
+
float MAX17043Component::get_setup_priority() const { return setup_priority::DATA; }
|
87
|
+
|
88
|
+
void MAX17043Component::sleep_mode() {
|
89
|
+
if (!this->is_failed()) {
|
90
|
+
if (!this->write_byte_16(MAX17043_CONFIG, MAX17043_CONFIG_POWER_UP_DEFAULT | MAX17043_CONFIG_SLEEP_MASK)) {
|
91
|
+
ESP_LOGW(TAG, "Unable to write the sleep bit to config register");
|
92
|
+
this->status_set_warning();
|
93
|
+
}
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
} // namespace max17043
|
98
|
+
} // namespace esphome
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include "esphome/core/component.h"
|
4
|
+
#include "esphome/components/sensor/sensor.h"
|
5
|
+
#include "esphome/components/i2c/i2c.h"
|
6
|
+
|
7
|
+
namespace esphome {
|
8
|
+
namespace max17043 {
|
9
|
+
|
10
|
+
class MAX17043Component : public PollingComponent, public i2c::I2CDevice {
|
11
|
+
public:
|
12
|
+
void setup() override;
|
13
|
+
void dump_config() override;
|
14
|
+
float get_setup_priority() const override;
|
15
|
+
void update() override;
|
16
|
+
void sleep_mode();
|
17
|
+
|
18
|
+
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
|
19
|
+
void set_battery_remaining_sensor(sensor::Sensor *battery_remaining_sensor) {
|
20
|
+
battery_remaining_sensor_ = battery_remaining_sensor;
|
21
|
+
}
|
22
|
+
|
23
|
+
protected:
|
24
|
+
sensor::Sensor *voltage_sensor_{nullptr};
|
25
|
+
sensor::Sensor *battery_remaining_sensor_{nullptr};
|
26
|
+
};
|
27
|
+
|
28
|
+
} // namespace max17043
|
29
|
+
} // namespace esphome
|
@@ -0,0 +1,77 @@
|
|
1
|
+
from esphome import automation
|
2
|
+
from esphome.automation import maybe_simple_id
|
3
|
+
import esphome.codegen as cg
|
4
|
+
from esphome.components import i2c, sensor
|
5
|
+
import esphome.config_validation as cv
|
6
|
+
from esphome.const import (
|
7
|
+
CONF_BATTERY_LEVEL,
|
8
|
+
CONF_BATTERY_VOLTAGE,
|
9
|
+
CONF_ID,
|
10
|
+
DEVICE_CLASS_BATTERY,
|
11
|
+
DEVICE_CLASS_VOLTAGE,
|
12
|
+
ENTITY_CATEGORY_DIAGNOSTIC,
|
13
|
+
STATE_CLASS_MEASUREMENT,
|
14
|
+
UNIT_PERCENT,
|
15
|
+
UNIT_VOLT,
|
16
|
+
)
|
17
|
+
|
18
|
+
DEPENDENCIES = ["i2c"]
|
19
|
+
|
20
|
+
max17043_ns = cg.esphome_ns.namespace("max17043")
|
21
|
+
MAX17043Component = max17043_ns.class_(
|
22
|
+
"MAX17043Component", cg.PollingComponent, i2c.I2CDevice
|
23
|
+
)
|
24
|
+
|
25
|
+
# Actions
|
26
|
+
SleepAction = max17043_ns.class_("SleepAction", automation.Action)
|
27
|
+
|
28
|
+
CONFIG_SCHEMA = (
|
29
|
+
cv.Schema(
|
30
|
+
{
|
31
|
+
cv.GenerateID(): cv.declare_id(MAX17043Component),
|
32
|
+
cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(
|
33
|
+
unit_of_measurement=UNIT_VOLT,
|
34
|
+
accuracy_decimals=3,
|
35
|
+
device_class=DEVICE_CLASS_VOLTAGE,
|
36
|
+
state_class=STATE_CLASS_MEASUREMENT,
|
37
|
+
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
38
|
+
),
|
39
|
+
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
|
40
|
+
unit_of_measurement=UNIT_PERCENT,
|
41
|
+
accuracy_decimals=3,
|
42
|
+
device_class=DEVICE_CLASS_BATTERY,
|
43
|
+
state_class=STATE_CLASS_MEASUREMENT,
|
44
|
+
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
45
|
+
),
|
46
|
+
}
|
47
|
+
)
|
48
|
+
.extend(cv.polling_component_schema("60s"))
|
49
|
+
.extend(i2c.i2c_device_schema(0x36))
|
50
|
+
)
|
51
|
+
|
52
|
+
|
53
|
+
async def to_code(config):
|
54
|
+
var = cg.new_Pvariable(config[CONF_ID])
|
55
|
+
await cg.register_component(var, config)
|
56
|
+
await i2c.register_i2c_device(var, config)
|
57
|
+
|
58
|
+
if voltage_config := config.get(CONF_BATTERY_VOLTAGE):
|
59
|
+
sens = await sensor.new_sensor(voltage_config)
|
60
|
+
cg.add(var.set_voltage_sensor(sens))
|
61
|
+
|
62
|
+
if CONF_BATTERY_LEVEL in config:
|
63
|
+
sens = await sensor.new_sensor(config[CONF_BATTERY_LEVEL])
|
64
|
+
cg.add(var.set_battery_remaining_sensor(sens))
|
65
|
+
|
66
|
+
|
67
|
+
MAX17043_ACTION_SCHEMA = maybe_simple_id(
|
68
|
+
{
|
69
|
+
cv.Required(CONF_ID): cv.use_id(MAX17043Component),
|
70
|
+
}
|
71
|
+
)
|
72
|
+
|
73
|
+
|
74
|
+
@automation.register_action("max17043.sleep_mode", SleepAction, MAX17043_ACTION_SCHEMA)
|
75
|
+
async def max17043_sleep_mode_to_code(config, action_id, template_arg, args):
|
76
|
+
paren = await cg.get_variable(config[CONF_ID])
|
77
|
+
return cg.new_Pvariable(action_id, template_arg, paren)
|
@@ -21,6 +21,7 @@ media_player_ns = cg.esphome_ns.namespace("media_player")
|
|
21
21
|
|
22
22
|
MediaPlayer = media_player_ns.class_("MediaPlayer")
|
23
23
|
|
24
|
+
|
24
25
|
PlayAction = media_player_ns.class_(
|
25
26
|
"PlayAction", automation.Action, cg.Parented.template(MediaPlayer)
|
26
27
|
)
|
@@ -60,7 +61,11 @@ AnnoucementTrigger = media_player_ns.class_(
|
|
60
61
|
"AnnouncementTrigger", automation.Trigger.template()
|
61
62
|
)
|
62
63
|
IsIdleCondition = media_player_ns.class_("IsIdleCondition", automation.Condition)
|
64
|
+
IsPausedCondition = media_player_ns.class_("IsPausedCondition", automation.Condition)
|
63
65
|
IsPlayingCondition = media_player_ns.class_("IsPlayingCondition", automation.Condition)
|
66
|
+
IsAnnouncingCondition = media_player_ns.class_(
|
67
|
+
"IsAnnouncingCondition", automation.Condition
|
68
|
+
)
|
64
69
|
|
65
70
|
|
66
71
|
async def setup_media_player_core_(var, config):
|
@@ -159,9 +164,15 @@ async def media_player_play_media_action(config, action_id, template_arg, args):
|
|
159
164
|
@automation.register_condition(
|
160
165
|
"media_player.is_idle", IsIdleCondition, MEDIA_PLAYER_ACTION_SCHEMA
|
161
166
|
)
|
167
|
+
@automation.register_condition(
|
168
|
+
"media_player.is_paused", IsPausedCondition, MEDIA_PLAYER_ACTION_SCHEMA
|
169
|
+
)
|
162
170
|
@automation.register_condition(
|
163
171
|
"media_player.is_playing", IsPlayingCondition, MEDIA_PLAYER_ACTION_SCHEMA
|
164
172
|
)
|
173
|
+
@automation.register_condition(
|
174
|
+
"media_player.is_announcing", IsAnnouncingCondition, MEDIA_PLAYER_ACTION_SCHEMA
|
175
|
+
)
|
165
176
|
async def media_player_action(config, action_id, template_arg, args):
|
166
177
|
var = cg.new_Pvariable(action_id, template_arg)
|
167
178
|
await cg.register_parented(var, config[CONF_ID])
|
@@ -68,5 +68,15 @@ template<typename... Ts> class IsPlayingCondition : public Condition<Ts...>, pub
|
|
68
68
|
bool check(Ts... x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_PLAYING; }
|
69
69
|
};
|
70
70
|
|
71
|
+
template<typename... Ts> class IsPausedCondition : public Condition<Ts...>, public Parented<MediaPlayer> {
|
72
|
+
public:
|
73
|
+
bool check(Ts... x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_PAUSED; }
|
74
|
+
};
|
75
|
+
|
76
|
+
template<typename... Ts> class IsAnnouncingCondition : public Condition<Ts...>, public Parented<MediaPlayer> {
|
77
|
+
public:
|
78
|
+
bool check(Ts... x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING; }
|
79
|
+
};
|
80
|
+
|
71
81
|
} // namespace media_player
|
72
82
|
} // namespace esphome
|
@@ -37,6 +37,10 @@ const char *media_player_command_to_string(MediaPlayerCommand command) {
|
|
37
37
|
return "UNMUTE";
|
38
38
|
case MEDIA_PLAYER_COMMAND_TOGGLE:
|
39
39
|
return "TOGGLE";
|
40
|
+
case MEDIA_PLAYER_COMMAND_VOLUME_UP:
|
41
|
+
return "VOLUME_UP";
|
42
|
+
case MEDIA_PLAYER_COMMAND_VOLUME_DOWN:
|
43
|
+
return "VOLUME_DOWN";
|
40
44
|
default:
|
41
45
|
return "UNKNOWN";
|
42
46
|
}
|
@@ -3,6 +3,8 @@
|
|
3
3
|
#include "esphome/core/log.h"
|
4
4
|
#include "air_conditioner.h"
|
5
5
|
#include "ac_adapter.h"
|
6
|
+
#include <cmath>
|
7
|
+
#include <cstdint>
|
6
8
|
|
7
9
|
namespace esphome {
|
8
10
|
namespace midea {
|
@@ -121,7 +123,21 @@ void AirConditioner::dump_config() {
|
|
121
123
|
|
122
124
|
void AirConditioner::do_follow_me(float temperature, bool beeper) {
|
123
125
|
#ifdef USE_REMOTE_TRANSMITTER
|
124
|
-
|
126
|
+
// Check if temperature is finite (not NaN or infinite)
|
127
|
+
if (!std::isfinite(temperature)) {
|
128
|
+
ESP_LOGW(Constants::TAG, "Follow me action requires a finite temperature, got: %f", temperature);
|
129
|
+
return;
|
130
|
+
}
|
131
|
+
|
132
|
+
// Round and convert temperature to long, then clamp and convert it to uint8_t
|
133
|
+
uint8_t temp_uint8 =
|
134
|
+
static_cast<uint8_t>(std::max(0L, std::min(static_cast<long>(UINT8_MAX), std::lroundf(temperature))));
|
135
|
+
|
136
|
+
ESP_LOGD(Constants::TAG, "Follow me action called with temperature: %f °C, rounded to: %u °C", temperature,
|
137
|
+
temp_uint8);
|
138
|
+
|
139
|
+
// Create and transmit the data
|
140
|
+
IrFollowMeData data(temp_uint8, beeper);
|
125
141
|
this->transmitter_.transmit(data);
|
126
142
|
#else
|
127
143
|
ESP_LOGW(Constants::TAG, "Action needs remote_transmitter component");
|