##################################################################################################### ##### NSPANEL ESPHOME created by Blackymas - https://github.com/Blackymas/NSPanel_HA_Blueprint ##### ##### ESPHome Add-on for Climate control - Shared - This will be called by heat/cool ##### ##### PLEASE only make changes if it is necessary and also the required knowledge is available. ##### ##### For normal use with the Blueprint, no changes are necessary. ##### ##################################################################################################### ##### ATTENTION: This will add climate elements to the core system and requires the core part. ##### ##################################################################################################### --- substitutions: ### Local thermostat defaults ### # https://esphome.io/components/climate/thermostat.html heater_relay: "0" # Select 1 for "Relay 1", 2 for "Relay 2" or "0" to a dummy switch/disabled cooler_relay: "0" # Select 1 for "Relay 1", 2 for "Relay 2" or "0" to a dummy switch/disabled min_off_time: "300" min_run_time: "300" min_idle_time: "30" # https://esphome.io/components/climate/index.html#base-climate-configuration temp_min: "7" temp_max: "35" temp_step: "0.5" target_low: "18" target_high: "24" cool_deadband: "0.5" # Temperature delta before engaging cooling cool_overrun: "0.5" # Temperature delta before disengaging cooling heat_deadband: "0.5" # Temperature delta before engaging heat heat_overrun: "0.5" # Temperature delta before disengaging heat ##### DO NOT CHANGE THIS ##### addon_climate_cool: "false" addon_climate_heat: "false" addon_climate_dual: "false" ############################## esphome: platformio_options: build_flags: - -D NSPANEL_HA_BLUEPRINT_ADDON_CLIMATE_BASE climate: - platform: thermostat name: Thermostat id: thermostat_embedded sensor: temp_nspanel min_idle_time: ${min_idle_time}s visual: min_temperature: ${temp_min} ${temp_units} max_temperature: ${temp_max} ${temp_units} temperature_step: target_temperature: 0.5 # This is hard coded for now as ESPHome isn't supporting a substitution here. In contact with support. current_temperature: 0.1 idle_action: - switch.turn_off: relay_${heater_relay} default_preset: "Off" on_boot_restore_from: memory internal: false on_state: - lambda: |- page_climate->execute(); page_home->execute(); globals: ##### Is embedded thermostat visible on climate page? ##### - id: is_addon_climate_visible type: bool restore_value: false initial_value: 'false' ##### Embeded climate friendly name ##### - id: addon_climate_friendly_name type: std::string restore_value: false initial_value: '"${name} Thermostat"' switch: ##### PHYSICAL SWITCH 0 (Dummy) - Used when relay is not set ##### - name: Relay 0 (dummy) platform: template id: relay_0 lambda: !lambda return false; internal: true optimistic: true script: - id: !extend change_climate_state then: - lambda: |- if (embedded and !id(is_uploading_tft)) { auto FahrenheitToCelsius = [](float fahrenheit) -> float { return (fahrenheit - 32.0) * 5.0 / 9.0; }; std::string temp_units = "${temp_units}"; bool temp_unit_fahrenheit = (temp_units == "°F" || temp_units == "F" || temp_units == "°f" || temp_units == "f"); auto call = thermostat_embedded->make_call(); float temperature; id(is_addon_climate_visible) = true; disp1->set_component_value("climate.embedded", 1); if (key == "temperature") { temperature = stof(value) / 10; if (temp_unit_fahrenheit) temperature = FahrenheitToCelsius(temperature); call.set_target_temperature(temperature); } else if (key == "target_temp_high") { temperature = stof(value) / 10; if (temp_unit_fahrenheit) temperature = FahrenheitToCelsius(temperature); call.set_target_temperature_high(temperature); } else if (key == "target_temp_low") { temperature = stof(value) / 10; if (temp_unit_fahrenheit) temperature = FahrenheitToCelsius(temperature); call.set_target_temperature_low(temperature); } else if (key == "hvac_mode") { call.set_mode(value); } call.perform(); } - id: !extend dump_config then: - lambda: |- // Check if more than one or none of the climate options are defined #if defined(NSPANEL_HA_BLUEPRINT_ADDON_CLIMATE_COOL) && defined(NSPANEL_HA_BLUEPRINT_ADDON_CLIMATE_HEAT) #error "Invalid settings for add-on Climate. More than one option selected: Cool + Heat." #elif defined(NSPANEL_HA_BLUEPRINT_ADDON_CLIMATE_COOL) && defined(NSPANEL_HA_BLUEPRINT_ADDON_CLIMATE_DUAL) #error "Invalid settings for add-on Climate. More than one option selected: Cool + Dual." #elif defined(NSPANEL_HA_BLUEPRINT_ADDON_CLIMATE_HEAT) && defined(NSPANEL_HA_BLUEPRINT_ADDON_CLIMATE_DUAL) #error "Invalid settings for add-on Climate. More than one option selected: Heat + Dual." #elif !defined(NSPANEL_HA_BLUEPRINT_ADDON_CLIMATE_COOL) && !defined(NSPANEL_HA_BLUEPRINT_ADDON_CLIMATE_HEAT) && !defined(NSPANEL_HA_BLUEPRINT_ADDON_CLIMATE_DUAL) #error "Invalid settings for add-on Climate. No option selected between Cool, Heat or Dual." #endif if (!id(is_uploading_tft)) { static const char *const TAG = "nspanel_ha_blueprint"; const uint cooler_relay = ${cooler_relay}; const uint heater_relay = ${heater_relay}; ESP_LOGCONFIG(TAG, "Add-on climate:"); #ifdef NSPANEL_HA_BLUEPRINT_ADDON_CLIMATE_COOL ESP_LOGCONFIG(TAG, " Cool: Enabled"); if (cooler_relay == 1 or cooler_relay == 2) ESP_LOGCONFIG(TAG, " Relay: %u", cooler_relay); else ESP_LOGE(TAG, " Relay: %u", cooler_relay); #endif #ifdef NSPANEL_HA_BLUEPRINT_ADDON_CLIMATE_HEAT ESP_LOGCONFIG(TAG, " Heat: Enabled"); if (heater_relay == 1 or heater_relay == 2) ESP_LOGCONFIG(TAG, " Relay: %u", heater_relay); else ESP_LOGE(TAG, " Relay: %u", heater_relay); #endif #ifdef NSPANEL_HA_BLUEPRINT_ADDON_CLIMATE_DUAL ESP_LOGCONFIG(TAG, " Dual: Enabled"); if (cooler_relay == 1 or cooler_relay == 2) ESP_LOGCONFIG(TAG, " Relay (cooler): %u", cooler_relay); else ESP_LOGE(TAG, " Relay (cooler): %u", cooler_relay); if (heater_relay == 1 or heater_relay == 2) ESP_LOGCONFIG(TAG, " Relay (heater): %u", heater_relay); else ESP_LOGE(TAG, " Relay (heater): %u", heater_relay); if (cooler_relay == heater_relay) ESP_LOGE(TAG, " Double relay assignment"); #endif } - id: !extend init_hardware_climate then: - lambda: |- id(addon_climate_friendly_name) = embedded_climate_friendly_name; - id: !extend page_climate then: - lambda: |- id(is_addon_climate_visible) = (current_page->state == "climate" and detailed_entity->state == "embedded_climate"); if (id(is_addon_climate_visible) and !id(is_uploading_tft)) { auto CelsiusToFahrenheit = [](float celsius) -> float { return (celsius * 9 / 5) + 32; }; const std::string temp_units = "${temp_units}"; const bool temp_unit_fahrenheit = (temp_units == "°F" || temp_units == "F" || temp_units == "°f" || temp_units == "f"); ClimateTraits traits = thermostat_embedded->get_traits(); disp1->set_component_text("page_label", id(addon_climate_friendly_name).c_str()); float temp_step = traits.get_visual_target_temperature_step(); float temp_offset = traits.get_visual_min_temperature(); float temp_max = traits.get_visual_max_temperature(); float temp_target = thermostat_embedded->target_temperature; float temp_target_high = thermostat_embedded->target_temperature_high; float temp_target_low = thermostat_embedded->target_temperature_low; float temp_current = thermostat_embedded->current_temperature; if (temp_unit_fahrenheit) { //temp_step = CelsiusToFahrenheit(temp_step); temp_step = std::ceil(temp_step * 1.8); temp_offset = CelsiusToFahrenheit(temp_offset); temp_max = CelsiusToFahrenheit(temp_max); temp_target = CelsiusToFahrenheit(temp_target); temp_target_high = CelsiusToFahrenheit(temp_target_high); temp_target_low = CelsiusToFahrenheit(temp_target_low); temp_current = CelsiusToFahrenheit(temp_current); } float total_steps = (temp_max-temp_offset)/temp_step; set_climate->execute ( temp_current, // current_temp 0, // supported_features ((${addon_climate_dual}) ? -999 : temp_target), // target_temp ((${addon_climate_dual}) ? temp_target_high : -999), // target_temp_high ((${addon_climate_dual}) ? temp_target_low : -999), // target_temp_low int(round(temp_step*10)), // temp_step int(round(total_steps)), // total_steps int(round(temp_offset*10)), // temp_offset "", // climate_icon true // embedded_climate ); // Update target temp icon update_climate_icon->execute("target_icon", int(thermostat_embedded->action), int(thermostat_embedded->mode)); // Update buttons bar // Hide not supported hotspots disp1->hide_component("button01"); if (${addon_climate_dual}) disp1->show_component("button02"); else disp1->hide_component("button02"); if (${addon_climate_heat} or ${addon_climate_dual}) disp1->show_component("button03"); else disp1->hide_component("button03"); //Heat if (${addon_climate_cool} or ${addon_climate_dual}) disp1->show_component("button04"); else disp1->hide_component("button04"); //Cool disp1->hide_component("button05"); disp1->hide_component("button06"); disp1->show_component("button07"); //Off // Set buttons colors disp1->set_component_font_color("button01", 6339); disp1->set_component_font_color("button02", (thermostat_embedded->mode==climate::CLIMATE_MODE_HEAT_COOL) ? 65535 : ((${addon_climate_dual}) ? 48631 : 6339)); disp1->set_component_font_color("button03", (thermostat_embedded->mode==climate::CLIMATE_MODE_HEAT) ? 64164 : ((${addon_climate_heat} or ${addon_climate_dual}) ? 48631 : 6339)); disp1->set_component_font_color("button04", (thermostat_embedded->mode==climate::CLIMATE_MODE_COOL) ? 1055 : ((${addon_climate_cool} or ${addon_climate_dual}) ? 48631 : 6339)); disp1->set_component_font_color("button05", 6339); disp1->set_component_font_color("button06", 6339); disp1->set_component_font_color("button07", (thermostat_embedded->mode==climate::CLIMATE_MODE_OFF) ? 10597 : 35921); } - id: !extend page_home then: - lambda: |- // Update chips if (id(is_embedded_thermostat) and !id(is_uploading_tft)) update_climate_icon->execute("home.chip_climate", int(thermostat_embedded->action), int(thermostat_embedded->mode)); - id: !extend set_climate then: - lambda: |- if (current_page->state == "climate" and !id(is_uploading_tft)) id(is_addon_climate_visible) = embedded_climate; ...