Edit esp-bedside-panel.yaml
This commit is contained in:
+227
-192
@@ -84,12 +84,12 @@ substitutions:
|
|||||||
# Device Naming
|
# Device Naming
|
||||||
device_name: "esp-bedside-panel"
|
device_name: "esp-bedside-panel"
|
||||||
friendly_name: "ESP Bedside Panel"
|
friendly_name: "ESP Bedside Panel"
|
||||||
description_comment: "Guition JC1060P470C_I_W_Y ESP32-P4 bedside control panel. Direct Quiet / Boost / Normal selection, Nano-X and aligned Climate controls. (Layout V1.1)"
|
description_comment: "Guition JC1060P470C_I_W_Y ESP32-P4 bedside control panel. Climate layout with guarded LVGL startup and additional memory headroom. (Layout V1.1)"
|
||||||
device_area: "Bedroom"
|
device_area: "Bedroom"
|
||||||
|
|
||||||
# Project Naming
|
# Project Naming
|
||||||
project_name: "Guition.JC1060P470C_I_W_Y"
|
project_name: "Guition.JC1060P470C_I_W_Y"
|
||||||
project_version: "v0.8q"
|
project_version: "v0.8r"
|
||||||
|
|
||||||
# Passwords & Secrets
|
# Passwords & Secrets
|
||||||
api_key: !secret esp-api_key
|
api_key: !secret esp-api_key
|
||||||
@@ -205,6 +205,18 @@ esphome:
|
|||||||
name: "${project_name}"
|
name: "${project_name}"
|
||||||
version: "${project_version}"
|
version: "${project_version}"
|
||||||
|
|
||||||
|
# Do not update LVGL widgets until page construction has completed.
|
||||||
|
on_boot:
|
||||||
|
priority: -100
|
||||||
|
then:
|
||||||
|
- delay: 2s
|
||||||
|
|
||||||
|
- lambda: |-
|
||||||
|
id(lvgl_ready) = true;
|
||||||
|
|
||||||
|
- script.execute: refresh_api_indicator
|
||||||
|
- script.execute: refresh_button_states
|
||||||
|
|
||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
# ESP PLATFORM AND FRAMEWORK:
|
# ESP PLATFORM AND FRAMEWORK:
|
||||||
# https://esphome.io/components/esp32.html
|
# https://esphome.io/components/esp32.html
|
||||||
@@ -278,9 +290,7 @@ api:
|
|||||||
level: INFO
|
level: INFO
|
||||||
format: "API connected: ESP Bedside Panel is online"
|
format: "API connected: ESP Bedside Panel is online"
|
||||||
|
|
||||||
- lvgl.widget.update:
|
- script.execute: refresh_api_indicator
|
||||||
id: bedroom_connection_indicator
|
|
||||||
bg_color: 0x65D483
|
|
||||||
|
|
||||||
# HA may deliver imported entity states before all LVGL widgets are ready.
|
# HA may deliver imported entity states before all LVGL widgets are ready.
|
||||||
# Run a delayed two-pass refresh so startup colours always match HA.
|
# Run a delayed two-pass refresh so startup colours always match HA.
|
||||||
@@ -295,18 +305,7 @@ api:
|
|||||||
# Allow another API client to remain connected before showing offline.
|
# Allow another API client to remain connected before showing offline.
|
||||||
- delay: 500ms
|
- delay: 500ms
|
||||||
|
|
||||||
- if:
|
- script.execute: refresh_api_indicator
|
||||||
condition:
|
|
||||||
api.connected:
|
|
||||||
then:
|
|
||||||
- lvgl.widget.update:
|
|
||||||
id: bedroom_connection_indicator
|
|
||||||
bg_color: 0x65D483
|
|
||||||
|
|
||||||
else:
|
|
||||||
- lvgl.widget.update:
|
|
||||||
id: bedroom_connection_indicator
|
|
||||||
bg_color: 0xF06A6A
|
|
||||||
|
|
||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
# OTA:
|
# OTA:
|
||||||
@@ -748,6 +747,12 @@ binary_sensor:
|
|||||||
# Local optimistic Heat Pump profile state
|
# Local optimistic Heat Pump profile state
|
||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
globals:
|
globals:
|
||||||
|
# Prevent state callbacks from touching widgets while LVGL creates pages.
|
||||||
|
- id: lvgl_ready
|
||||||
|
type: bool
|
||||||
|
restore_value: false
|
||||||
|
initial_value: "false"
|
||||||
|
|
||||||
# 0 = Normal, 1 = Quiet, 2 = Boost.
|
# 0 = Normal, 1 = Quiet, 2 = Boost.
|
||||||
- id: climate_profile_optimistic
|
- id: climate_profile_optimistic
|
||||||
type: int
|
type: int
|
||||||
@@ -759,6 +764,30 @@ globals:
|
|||||||
# Refresh all state-dependent LVGL button colours
|
# Refresh all state-dependent LVGL button colours
|
||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
script:
|
script:
|
||||||
|
# Update the API status indicator only after LVGL has finished constructing.
|
||||||
|
- id: refresh_api_indicator
|
||||||
|
mode: restart
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: |-
|
||||||
|
return id(lvgl_ready);
|
||||||
|
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
api.connected:
|
||||||
|
|
||||||
|
then:
|
||||||
|
- lvgl.widget.update:
|
||||||
|
id: bedroom_connection_indicator
|
||||||
|
bg_color: 0x65D483
|
||||||
|
|
||||||
|
else:
|
||||||
|
- lvgl.widget.update:
|
||||||
|
id: bedroom_connection_indicator
|
||||||
|
bg_color: 0xF06A6A
|
||||||
|
|
||||||
# Immediate refresh using the latest imported Home Assistant states.
|
# Immediate refresh using the latest imported Home Assistant states.
|
||||||
- id: refresh_button_states
|
- id: refresh_button_states
|
||||||
mode: restart
|
mode: restart
|
||||||
@@ -863,196 +892,202 @@ script:
|
|||||||
- id: refresh_climate_controls
|
- id: refresh_climate_controls
|
||||||
mode: restart
|
mode: restart
|
||||||
then:
|
then:
|
||||||
# Heat-pump mode buttons. The requested mode wins while a change is
|
- if:
|
||||||
# pending; when it is "none", the actual climate state is used.
|
condition:
|
||||||
- lvgl.widget.update:
|
lambda: |-
|
||||||
id: climate_off_button
|
return id(lvgl_ready);
|
||||||
bg_color: !lambda |-
|
|
||||||
const auto requested = id(ha_climate_requested_mode).state;
|
|
||||||
const auto current = id(ha_climate_current_mode).state;
|
|
||||||
|
|
||||||
const bool requested_none = (
|
then:
|
||||||
requested.empty() ||
|
# Heat-pump mode buttons. The requested mode wins while a change is
|
||||||
requested == "none" ||
|
# pending; when it is "none", the actual climate state is used.
|
||||||
requested == "None" ||
|
- lvgl.widget.update:
|
||||||
requested == "unknown" ||
|
id: climate_off_button
|
||||||
requested == "unavailable"
|
bg_color: !lambda |-
|
||||||
);
|
const auto requested = id(ha_climate_requested_mode).state;
|
||||||
|
const auto current = id(ha_climate_current_mode).state;
|
||||||
|
|
||||||
const bool active = (
|
const bool requested_none = (
|
||||||
requested == "off" ||
|
requested.empty() ||
|
||||||
requested == "Off" ||
|
requested == "none" ||
|
||||||
(requested_none && current == "off")
|
requested == "None" ||
|
||||||
);
|
requested == "unknown" ||
|
||||||
|
requested == "unavailable"
|
||||||
|
);
|
||||||
|
|
||||||
return active
|
const bool active = (
|
||||||
? lv_color_hex(0x548C6C)
|
requested == "off" ||
|
||||||
: lv_color_hex(0x28313D);
|
requested == "Off" ||
|
||||||
|
(requested_none && current == "off")
|
||||||
|
);
|
||||||
|
|
||||||
- lvgl.widget.update:
|
return active
|
||||||
id: climate_heat_button
|
? lv_color_hex(0x548C6C)
|
||||||
bg_color: !lambda |-
|
: lv_color_hex(0x28313D);
|
||||||
const auto requested = id(ha_climate_requested_mode).state;
|
|
||||||
const auto current = id(ha_climate_current_mode).state;
|
|
||||||
|
|
||||||
const bool requested_none = (
|
- lvgl.widget.update:
|
||||||
requested.empty() ||
|
id: climate_heat_button
|
||||||
requested == "none" ||
|
bg_color: !lambda |-
|
||||||
requested == "None" ||
|
const auto requested = id(ha_climate_requested_mode).state;
|
||||||
requested == "unknown" ||
|
const auto current = id(ha_climate_current_mode).state;
|
||||||
requested == "unavailable"
|
|
||||||
);
|
|
||||||
|
|
||||||
const bool active = (
|
const bool requested_none = (
|
||||||
requested == "heat" ||
|
requested.empty() ||
|
||||||
requested == "Heat" ||
|
requested == "none" ||
|
||||||
(requested_none && current == "heat")
|
requested == "None" ||
|
||||||
);
|
requested == "unknown" ||
|
||||||
|
requested == "unavailable"
|
||||||
|
);
|
||||||
|
|
||||||
return active
|
const bool active = (
|
||||||
? lv_color_hex(0xB76822)
|
requested == "heat" ||
|
||||||
: lv_color_hex(0x28313D);
|
requested == "Heat" ||
|
||||||
|
(requested_none && current == "heat")
|
||||||
|
);
|
||||||
|
|
||||||
- lvgl.widget.update:
|
return active
|
||||||
id: climate_cool_button
|
? lv_color_hex(0xB76822)
|
||||||
bg_color: !lambda |-
|
: lv_color_hex(0x28313D);
|
||||||
const auto requested = id(ha_climate_requested_mode).state;
|
|
||||||
const auto current = id(ha_climate_current_mode).state;
|
|
||||||
|
|
||||||
const bool requested_none = (
|
- lvgl.widget.update:
|
||||||
requested.empty() ||
|
id: climate_cool_button
|
||||||
requested == "none" ||
|
bg_color: !lambda |-
|
||||||
requested == "None" ||
|
const auto requested = id(ha_climate_requested_mode).state;
|
||||||
requested == "unknown" ||
|
const auto current = id(ha_climate_current_mode).state;
|
||||||
requested == "unavailable"
|
|
||||||
);
|
|
||||||
|
|
||||||
const bool active = (
|
const bool requested_none = (
|
||||||
requested == "cool" ||
|
requested.empty() ||
|
||||||
requested == "Cool" ||
|
requested == "none" ||
|
||||||
(requested_none && current == "cool")
|
requested == "None" ||
|
||||||
);
|
requested == "unknown" ||
|
||||||
|
requested == "unavailable"
|
||||||
|
);
|
||||||
|
|
||||||
return active
|
const bool active = (
|
||||||
? lv_color_hex(0x2E6A96)
|
requested == "cool" ||
|
||||||
: lv_color_hex(0x28313D);
|
requested == "Cool" ||
|
||||||
|
(requested_none && current == "cool")
|
||||||
|
);
|
||||||
|
|
||||||
- lvgl.widget.update:
|
return active
|
||||||
id: climate_dry_button
|
? lv_color_hex(0x2E6A96)
|
||||||
bg_color: !lambda |-
|
: lv_color_hex(0x28313D);
|
||||||
const auto requested = id(ha_climate_requested_mode).state;
|
|
||||||
const auto current = id(ha_climate_current_mode).state;
|
|
||||||
|
|
||||||
const bool requested_none = (
|
- lvgl.widget.update:
|
||||||
requested.empty() ||
|
id: climate_dry_button
|
||||||
requested == "none" ||
|
bg_color: !lambda |-
|
||||||
requested == "None" ||
|
const auto requested = id(ha_climate_requested_mode).state;
|
||||||
requested == "unknown" ||
|
const auto current = id(ha_climate_current_mode).state;
|
||||||
requested == "unavailable"
|
|
||||||
);
|
|
||||||
|
|
||||||
const bool active = (
|
const bool requested_none = (
|
||||||
requested == "dry" ||
|
requested.empty() ||
|
||||||
requested == "Dry" ||
|
requested == "none" ||
|
||||||
requested == "dehumidify" ||
|
requested == "None" ||
|
||||||
requested == "Dehumidify" ||
|
requested == "unknown" ||
|
||||||
(
|
requested == "unavailable"
|
||||||
requested_none &&
|
);
|
||||||
(
|
|
||||||
current == "dry" ||
|
const bool active = (
|
||||||
current == "dehumidify"
|
requested == "dry" ||
|
||||||
|
requested == "Dry" ||
|
||||||
|
requested == "dehumidify" ||
|
||||||
|
requested == "Dehumidify" ||
|
||||||
|
(
|
||||||
|
requested_none &&
|
||||||
|
(
|
||||||
|
current == "dry" ||
|
||||||
|
current == "dehumidify"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return active
|
||||||
|
? lv_color_hex(0x32677F)
|
||||||
|
: lv_color_hex(0x28313D);
|
||||||
|
|
||||||
|
# Quiet / Boost are mutually exclusive. Normal means neither is lit.
|
||||||
|
- lvgl.widget.update:
|
||||||
|
id: climate_quiet_button
|
||||||
|
bg_color: !lambda |-
|
||||||
|
return id(climate_profile_optimistic) == 1
|
||||||
|
? lv_color_hex(0x315843)
|
||||||
|
: lv_color_hex(0x28313D);
|
||||||
|
|
||||||
|
- lvgl.widget.update:
|
||||||
|
id: climate_boost_button
|
||||||
|
bg_color: !lambda |-
|
||||||
|
return id(climate_profile_optimistic) == 2
|
||||||
|
? lv_color_hex(0x6B4A86)
|
||||||
|
: lv_color_hex(0x28313D);
|
||||||
|
|
||||||
|
- lvgl.widget.update:
|
||||||
|
id: climate_nanoe_button
|
||||||
|
bg_color: !lambda |-
|
||||||
|
return id(ha_nanoe).state
|
||||||
|
? lv_color_hex(0x315E82)
|
||||||
|
: lv_color_hex(0x28313D);
|
||||||
|
|
||||||
|
# Heat-pump target setpoint.
|
||||||
|
- lvgl.label.update:
|
||||||
|
id: climate_setpoint_value_label
|
||||||
|
text: !lambda |-
|
||||||
|
const float value = id(ha_climate_setpoint).state;
|
||||||
|
|
||||||
|
if (isnan(value)) {
|
||||||
|
return std::string("--.-°C");
|
||||||
|
}
|
||||||
|
|
||||||
|
char buffer[16];
|
||||||
|
snprintf(buffer, sizeof(buffer), "%.1f°C", value);
|
||||||
|
return std::string(buffer);
|
||||||
|
|
||||||
|
# One-button ceiling-fan cycle: Off -> 1 -> 2 -> 3 -> Off.
|
||||||
|
- lvgl.label.update:
|
||||||
|
id: ceiling_fan_cycle_label
|
||||||
|
text: !lambda |-
|
||||||
|
if (!id(ha_ceiling_fan_on).state) {
|
||||||
|
return std::string("Ceiling Fan: Off");
|
||||||
|
}
|
||||||
|
|
||||||
|
const float percentage = id(ha_ceiling_fan_percentage).state;
|
||||||
|
|
||||||
|
if (isnan(percentage) || percentage < 50.0f) {
|
||||||
|
return std::string("Ceiling Fan: 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (percentage < 85.0f) {
|
||||||
|
return std::string("Ceiling Fan: 2");
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::string("Ceiling Fan: 3");
|
||||||
|
|
||||||
|
- lvgl.widget.update:
|
||||||
|
id: ceiling_fan_cycle_button
|
||||||
|
bg_color: !lambda |-
|
||||||
|
return id(ha_ceiling_fan_on).state
|
||||||
|
? lv_color_hex(0x315E82)
|
||||||
|
: lv_color_hex(0x28313D);
|
||||||
|
|
||||||
|
# Air Filter Quiet is highlighted for filter levels 0, 1 or 2.
|
||||||
|
- lvgl.widget.update:
|
||||||
|
id: air_filter_quiet_button
|
||||||
|
bg_color: !lambda |-
|
||||||
|
const float level = id(ha_air_filter_level).state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
!isnan(level) &&
|
||||||
|
level >= 0.0f &&
|
||||||
|
level <= 2.0f
|
||||||
)
|
)
|
||||||
)
|
? lv_color_hex(0x315843)
|
||||||
);
|
: lv_color_hex(0x28313D);
|
||||||
|
|
||||||
return active
|
# Climate 9pm-12 follows the Night Power helper state.
|
||||||
? lv_color_hex(0x32677F)
|
- lvgl.widget.update:
|
||||||
: lv_color_hex(0x28313D);
|
id: climate_night_power_button
|
||||||
|
bg_color: !lambda |-
|
||||||
# Quiet / Boost are mutually exclusive. Normal means neither is lit.
|
return id(ha_climate_night_power).state
|
||||||
- lvgl.widget.update:
|
? lv_color_hex(0x5A4778)
|
||||||
id: climate_quiet_button
|
: lv_color_hex(0x28313D);
|
||||||
bg_color: !lambda |-
|
|
||||||
return id(climate_profile_optimistic) == 1
|
|
||||||
? lv_color_hex(0x315843)
|
|
||||||
: lv_color_hex(0x28313D);
|
|
||||||
|
|
||||||
- lvgl.widget.update:
|
|
||||||
id: climate_boost_button
|
|
||||||
bg_color: !lambda |-
|
|
||||||
return id(climate_profile_optimistic) == 2
|
|
||||||
? lv_color_hex(0x6B4A86)
|
|
||||||
: lv_color_hex(0x28313D);
|
|
||||||
|
|
||||||
- lvgl.widget.update:
|
|
||||||
id: climate_nanoe_button
|
|
||||||
bg_color: !lambda |-
|
|
||||||
return id(ha_nanoe).state
|
|
||||||
? lv_color_hex(0x315E82)
|
|
||||||
: lv_color_hex(0x28313D);
|
|
||||||
|
|
||||||
# Heat-pump target setpoint.
|
|
||||||
- lvgl.label.update:
|
|
||||||
id: climate_setpoint_value_label
|
|
||||||
text: !lambda |-
|
|
||||||
const float value = id(ha_climate_setpoint).state;
|
|
||||||
|
|
||||||
if (isnan(value)) {
|
|
||||||
return std::string("--.-°C");
|
|
||||||
}
|
|
||||||
|
|
||||||
char buffer[16];
|
|
||||||
snprintf(buffer, sizeof(buffer), "%.1f°C", value);
|
|
||||||
return std::string(buffer);
|
|
||||||
|
|
||||||
# One-button ceiling-fan cycle: Off -> 1 -> 2 -> 3 -> Off.
|
|
||||||
- lvgl.label.update:
|
|
||||||
id: ceiling_fan_cycle_label
|
|
||||||
text: !lambda |-
|
|
||||||
if (!id(ha_ceiling_fan_on).state) {
|
|
||||||
return std::string("Ceiling Fan: Off");
|
|
||||||
}
|
|
||||||
|
|
||||||
const float percentage = id(ha_ceiling_fan_percentage).state;
|
|
||||||
|
|
||||||
if (isnan(percentage) || percentage < 50.0f) {
|
|
||||||
return std::string("Ceiling Fan: 1");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (percentage < 85.0f) {
|
|
||||||
return std::string("Ceiling Fan: 2");
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::string("Ceiling Fan: 3");
|
|
||||||
|
|
||||||
- lvgl.widget.update:
|
|
||||||
id: ceiling_fan_cycle_button
|
|
||||||
bg_color: !lambda |-
|
|
||||||
return id(ha_ceiling_fan_on).state
|
|
||||||
? lv_color_hex(0x315E82)
|
|
||||||
: lv_color_hex(0x28313D);
|
|
||||||
|
|
||||||
# Air Filter Quiet is highlighted for filter levels 0, 1 or 2.
|
|
||||||
- lvgl.widget.update:
|
|
||||||
id: air_filter_quiet_button
|
|
||||||
bg_color: !lambda |-
|
|
||||||
const float level = id(ha_air_filter_level).state;
|
|
||||||
|
|
||||||
return (
|
|
||||||
!isnan(level) &&
|
|
||||||
level >= 0.0f &&
|
|
||||||
level <= 2.0f
|
|
||||||
)
|
|
||||||
? lv_color_hex(0x315843)
|
|
||||||
: lv_color_hex(0x28313D);
|
|
||||||
|
|
||||||
# Climate 9pm-12 follows the Night Power helper state.
|
|
||||||
- lvgl.widget.update:
|
|
||||||
id: climate_night_power_button
|
|
||||||
bg_color: !lambda |-
|
|
||||||
return id(ha_climate_night_power).state
|
|
||||||
? lv_color_hex(0x5A4778)
|
|
||||||
: lv_color_hex(0x28313D);
|
|
||||||
|
|
||||||
# Two passes allow time for LVGL creation and Home Assistant state delivery.
|
# Two passes allow time for LVGL creation and Home Assistant state delivery.
|
||||||
- id: startup_button_state_sync
|
- id: startup_button_state_sync
|
||||||
@@ -1184,7 +1219,7 @@ lvgl:
|
|||||||
long_press_repeat_time: 500ms
|
long_press_repeat_time: 500ms
|
||||||
|
|
||||||
rotation: 0
|
rotation: 0
|
||||||
buffer_size: 8%
|
buffer_size: 6%
|
||||||
log_level: WARN
|
log_level: WARN
|
||||||
default_font: font_button
|
default_font: font_button
|
||||||
page_wrap: false
|
page_wrap: false
|
||||||
|
|||||||
Reference in New Issue
Block a user