diff --git a/esphome/esp-bedside-panel.yaml b/esphome/esp-bedside-panel.yaml index ccdd03b..7041c33 100644 --- a/esphome/esp-bedside-panel.yaml +++ b/esphome/esp-bedside-panel.yaml @@ -84,12 +84,12 @@ substitutions: # Device Naming device_name: "esp-bedside-panel" friendly_name: "ESP Bedside Panel" - description_comment: "Guition JC1060P470C_I_W_Y ESP32-P4 bedside control panel. Reliable setpoint icons and optimistic Quiet / Boost / None profile controls. (Layout V1.1)" + 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)" device_area: "Bedroom" # Project Naming project_name: "Guition.JC1060P470C_I_W_Y" - project_version: "v0.8p" + project_version: "v0.8q" # Passwords & Secrets api_key: !secret esp-api_key @@ -147,12 +147,12 @@ substitutions: climate_cool_script: "script.master_bedroom_hvac_cool" climate_dry_script: "script.master_bedroom_hvac_dry" climate_setpoint_adjust_script: "script.master_bedroom_setpoint_adjust" - climate_profile_next_script: "script.master_bedroom_heat_pump_profile_next" # Home Assistant Entities - Ceiling Fan / Air Filter / Night Power ceiling_fan_entity: "fan.tasmo_ifan02_3793_bedrm1_1" air_filter_level_entity: "number.bedroom_1_air_filter_bedroom_1_air_filter_speed_level" climate_night_power_entity: "input_boolean.master_bedroom_night_power_mode" + nanoe_entity: "switch.master_bedroom_nanoe" #:########################################################################################:# # PACKAGES: @@ -715,6 +715,14 @@ binary_sensor: then: - script.execute: refresh_climate_controls + - platform: homeassistant + id: ha_nanoe + entity_id: "${nanoe_entity}" + internal: true + on_state: + then: + - script.execute: refresh_climate_controls + - platform: homeassistant id: ha_quiet_time entity_id: "${quiet_time_entity}" @@ -740,13 +748,8 @@ binary_sensor: # Local optimistic Heat Pump profile state #:########################################################################################:# globals: - # -1 = follow HA, 0 = None, 1 = Quiet, 2 = Boost / Boost. + # 0 = Normal, 1 = Quiet, 2 = Boost. - id: climate_profile_optimistic - type: int - restore_value: false - initial_value: "-1" - - - id: climate_profile_cycle_attempts type: int restore_value: false initial_value: "0" @@ -966,73 +969,28 @@ script: ? lv_color_hex(0x32677F) : lv_color_hex(0x28313D); - # Quiet / Boost are mutually exclusive. While a button action is - # pending, the optimistic local value is shown immediately. Otherwise, - # the HA profile helper is authoritative, with actual preset as fallback. + # Quiet / Boost are mutually exclusive. Normal means neither is lit. - lvgl.widget.update: id: climate_quiet_button bg_color: !lambda |- - int selected = id(climate_profile_optimistic); - - if (selected < 0) { - const auto profile = id(ha_climate_profile).state; - const auto preset = id(ha_climate_preset_mode).state; - - if (profile == "Quiet" || profile == "quiet") { - selected = 1; - } else if ( - profile == "Boost" || - profile == "boost" - ) { - selected = 2; - } else if (preset == "quiet" || preset == "Quiet") { - selected = 1; - } else if ( - preset == "boost" || - preset == "Boost" - ) { - selected = 2; - } else { - selected = 0; - } - } - - return selected == 1 + return id(climate_profile_optimistic) == 1 ? lv_color_hex(0x315843) : lv_color_hex(0x28313D); - lvgl.widget.update: id: climate_boost_button bg_color: !lambda |- - int selected = id(climate_profile_optimistic); - - if (selected < 0) { - const auto profile = id(ha_climate_profile).state; - const auto preset = id(ha_climate_preset_mode).state; - - if (profile == "Quiet" || profile == "quiet") { - selected = 1; - } else if ( - profile == "Boost" || - profile == "boost" - ) { - selected = 2; - } else if (preset == "quiet" || preset == "Quiet") { - selected = 1; - } else if ( - preset == "boost" || - preset == "Boost" - ) { - selected = 2; - } else { - selected = 0; - } - } - - return selected == 2 + 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 @@ -1096,65 +1054,6 @@ script: ? lv_color_hex(0x5A4778) : lv_color_hex(0x28313D); - # Apply the optimistic target through the same HA profile-cycle script used - # by the existing dashboard. It checks after each cycle, so no cycle order - # is assumed. At most three cycles are attempted. - - id: apply_climate_profile_target - mode: restart - then: - - lambda: |- - id(climate_profile_cycle_attempts) = 0; - - - while: - condition: - lambda: |- - if (id(climate_profile_cycle_attempts) >= 3) { - return false; - } - - int current = 0; - const auto profile = id(ha_climate_profile).state; - const auto preset = id(ha_climate_preset_mode).state; - - if (profile == "Quiet" || profile == "quiet") { - current = 1; - } else if ( - profile == "Boost" || - profile == "boost" - ) { - current = 2; - } else if (preset == "quiet" || preset == "Quiet") { - current = 1; - } else if ( - preset == "boost" || - preset == "Boost" - ) { - current = 2; - } - - return current != id(climate_profile_optimistic); - - then: - - homeassistant.action: - action: script.turn_on - data: - entity_id: "${climate_profile_next_script}" - - - lambda: |- - id(climate_profile_cycle_attempts) += 1; - - # Allow the HA script and Panasonic integration to update the - # profile helper before deciding whether another cycle is needed. - - delay: 1800ms - - # Keep the optimistic highlight briefly, then return to authoritative HA. - - delay: 1500ms - - - lambda: |- - id(climate_profile_optimistic) = -1; - - - script.execute: refresh_climate_controls - # Two passes allow time for LVGL creation and Home Assistant state delivery. - id: startup_button_state_sync mode: restart @@ -1234,23 +1133,22 @@ text_sensor: then: - script.execute: refresh_climate_controls - # Existing HA profile helper uses values such as Quiet and Boost. + # Authoritative Heat Pump profile helper: Boost / Quiet / Normal. - platform: homeassistant id: ha_climate_profile entity_id: "${climate_profile_entity}" internal: true on_value: then: - - script.execute: refresh_climate_controls + - lambda: |- + if (x == "Quiet" || x == "quiet") { + id(climate_profile_optimistic) = 1; + } else if (x == "Boost" || x == "boost") { + id(climate_profile_optimistic) = 2; + } else { + id(climate_profile_optimistic) = 0; + } - # Actual climate preset provides an additional authoritative profile state. - - platform: homeassistant - id: ha_climate_preset_mode - entity_id: "${climate_entity}" - attribute: preset_mode - internal: true - on_value: - then: - script.execute: refresh_climate_controls - platform: version @@ -1912,12 +1810,12 @@ lvgl: entity_id: "${climate_dry_script}" #:################################################################################:# - # HEAT-PUMP PROFILE / SETPOINT + # HEAT-PUMP PROFILE / NANO-X / SETPOINT #:################################################################################:# - label: - x: 535 + x: 625 y: 187 - width: 270 + width: 230 text: "Setpoint" text_font: font_small text_align: CENTER @@ -1927,7 +1825,7 @@ lvgl: id: climate_quiet_button x: 25 y: 218 - width: 170 + width: 145 height: 105 bg_color: 0x28313D bg_opa: COVER @@ -1943,44 +1841,37 @@ lvgl: text_font: font_button text_color: 0xFFFFFF on_click: - # Quiet -> None when already active; otherwise select Quiet. + # Quiet toggles between Quiet and Normal. - lambda: |- - int selected = id(climate_profile_optimistic); - - if (selected < 0) { - const auto profile = id(ha_climate_profile).state; - const auto preset = id(ha_climate_preset_mode).state; - - if ( - profile == "Quiet" || - profile == "quiet" || - preset == "Quiet" || - preset == "quiet" - ) { - selected = 1; - } else if ( - profile == "Boost" || - profile == "boost" || - preset == "Boost" || - preset == "boost" - ) { - selected = 2; - } else { - selected = 0; - } - } - id(climate_profile_optimistic) = - selected == 1 ? 0 : 1; + id(climate_profile_optimistic) == 1 ? 0 : 1; - script.execute: refresh_climate_controls - - script.execute: apply_climate_profile_target + + - if: + condition: + lambda: |- + return id(climate_profile_optimistic) == 1; + + then: + - homeassistant.action: + action: input_select.select_option + data: + entity_id: "${climate_profile_entity}" + option: "Quiet" + + else: + - homeassistant.action: + action: input_select.select_option + data: + entity_id: "${climate_profile_entity}" + option: "Normal" - button: id: climate_boost_button - x: 205 + x: 180 y: 218 - width: 170 + width: 145 height: 105 bg_color: 0x28313D bg_opa: COVER @@ -1996,44 +1887,62 @@ lvgl: text_font: font_button text_color: 0xFFFFFF on_click: - # Boost -> None when already active; otherwise select Boost. + # Boost toggles between Boost and Normal. - lambda: |- - int selected = id(climate_profile_optimistic); - - if (selected < 0) { - const auto profile = id(ha_climate_profile).state; - const auto preset = id(ha_climate_preset_mode).state; - - if ( - profile == "Quiet" || - profile == "quiet" || - preset == "Quiet" || - preset == "quiet" - ) { - selected = 1; - } else if ( - profile == "Boost" || - profile == "boost" || - preset == "Boost" || - preset == "boost" - ) { - selected = 2; - } else { - selected = 0; - } - } - id(climate_profile_optimistic) = - selected == 2 ? 0 : 2; + id(climate_profile_optimistic) == 2 ? 0 : 2; - script.execute: refresh_climate_controls - - script.execute: apply_climate_profile_target + + - if: + condition: + lambda: |- + return id(climate_profile_optimistic) == 2; + + then: + - homeassistant.action: + action: input_select.select_option + data: + entity_id: "${climate_profile_entity}" + option: "Boost" + + else: + - homeassistant.action: + action: input_select.select_option + data: + entity_id: "${climate_profile_entity}" + option: "Normal" + + - button: + id: climate_nanoe_button + x: 335 + y: 218 + width: 145 + height: 105 + bg_color: 0x28313D + bg_opa: COVER + border_width: 2 + border_color: 0x47789B + radius: 18 + pressed: + bg_color: 0x315E82 + widgets: + - label: + align: CENTER + text: "Nano-X" + text_font: font_button + text_color: 0xFFFFFF + on_click: + - homeassistant.action: + action: switch.toggle + data: + entity_id: "${nanoe_entity}" - button: id: climate_setpoint_down_button - x: 405 + x: 505 y: 218 - width: 120 + width: 110 height: 105 bg_color: 0x28313D bg_opa: COVER @@ -2056,9 +1965,9 @@ lvgl: - button: id: climate_setpoint_display - x: 535 + x: 625 y: 218 - width: 270 + width: 230 height: 105 bg_color: 0x1C2632 bg_opa: COVER @@ -2075,7 +1984,7 @@ lvgl: - button: id: climate_setpoint_up_button - x: 815 + x: 865 y: 218 width: 120 height: 105 @@ -2105,7 +2014,7 @@ lvgl: id: ceiling_fan_cycle_button x: 25 y: 356 - width: 310 + width: 306 height: 100 bg_color: 0x28313D bg_opa: COVER @@ -2181,9 +2090,9 @@ lvgl: - button: id: air_filter_quiet_button - x: 357 + x: 352 y: 356 - width: 310 + width: 306 height: 100 bg_color: 0x28313D bg_opa: COVER @@ -2207,9 +2116,9 @@ lvgl: - button: id: climate_night_power_button - x: 689 + x: 679 y: 356 - width: 310 + width: 307 height: 100 bg_color: 0x28313D bg_opa: COVER @@ -2230,23 +2139,26 @@ lvgl: data: entity_id: "${climate_night_power_entity}" + #:################################################################################:# + # BOTTOM NAVIGATION + #:################################################################################:# - button: id: climate_back_button - x: 365 - y: 490 - width: 294 - height: 78 + x: 612 + y: 475 + width: 190 + height: 92 bg_color: 0x315843 bg_opa: COVER border_width: 2 border_color: 0x548C6C - radius: 18 + radius: 16 pressed: bg_color: 0x416F55 widgets: - label: align: CENTER - text: "Back to Bedroom" + text: "Back" text_font: font_button text_color: 0xFFFFFF on_click: @@ -2255,6 +2167,36 @@ lvgl: animation: MOVE_RIGHT time: 220ms + - button: + id: climate_clock_button + x: 810 + y: 475 + width: 190 + height: 92 + bg_color: 0x214F72 + bg_opa: COVER + border_width: 2 + border_color: 0x3E7FA7 + radius: 16 + pressed: + bg_color: 0x2E6A96 + widgets: + - label: + align: CENTER + text: "Clock" + text_font: font_button + text_color: 0xFFFFFF + on_click: + - lvgl.page.show: + id: clock_page + animation: FADE_IN + time: 250ms + + - light.turn_on: + id: display_backlight + brightness: "${clock_brightness}" + transition_length: 1500ms + #:####################################################################################:# # CLOCK PAGE #:####################################################################################:#