Moving service calls back to the blueprint as latest HA requires user's permissions to call HA services from ESPHome. This still have to be used for Alarm calls due to security concerns.
1681 lines
65 KiB
YAML
1681 lines
65 KiB
YAML
#####################################################################################################
|
|
##### NSPANEL ESPHOME created by Blackymas - https://github.com/Blackymas/NSPanel_HA_Blueprint #####
|
|
##### ADVANCED CONFIG + FULL ESPHOME CODE! #####
|
|
##### 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. #####
|
|
#####################################################################################################
|
|
|
|
|
|
##### ADVANCED CONFIGURATION - activate only when you know what you do ##############################
|
|
substitutions:
|
|
verbose_log: "false"
|
|
baud_rate: "115200" # requires 115200 if tft is installed but can be changed to 9600 if tft upload fails and nextion switches to 9600
|
|
# ## usage of secrets-file ## -> comment in ###### Change ME ######
|
|
# device_name: "nspanel-name" # Wird im Blueprint benötigt!
|
|
# wifi_ssid: !secret nspanel_wifi_ssid # add in your esphome secrets file.
|
|
# wifi_password: !secret nspanel_wifi_password # add in your esphome secrets file. -> per default this is also used for ota_password and web_password
|
|
# ota_password: !secret nspanel_ota_password # add in your esphome secrets file. - manual change in code required to activate
|
|
# web_password: !secret nspanel_web_password # add in your esphome secrets file. - manual change in code required to activate
|
|
# api_password: !secret nspanel_api_password # add in your esphome secrets file. - manual change in code required to activate
|
|
# nextion_update_url: !secret nspanel_update_url # add in your esphome secrets file. Example: "http://"HOME ASSISTANT IP":8123/local/nspanel/nspanel.tft"
|
|
|
|
# ## static ip config ##
|
|
# ip: "10.0.0.7"
|
|
# gw: "10.0.0.138"
|
|
# subnet: "255.255.255.0"
|
|
# dns: "10.0.0.138"
|
|
# domain: ".local"
|
|
|
|
time_source: "homeassistant" # Either "homeassistant" or "sntp" are supported
|
|
|
|
### Local thermostat defaults ###
|
|
# https://esphome.io/components/climate/thermostat.html
|
|
embedded_thermostat_disabled: "true"
|
|
embedded_thermostat_temp_units: "°C"
|
|
embedded_thermostat_heater_relay: "1" # Select 1 for "Relay 1" or 2 for "Relay 2"
|
|
embedded_thermostat_min_heating_off_time: "300"
|
|
embedded_thermostat_min_heating_run_time: "300"
|
|
embedded_thermostat_min_idle_time: "30"
|
|
# https://esphome.io/components/climate/index.html#base-climate-configuration
|
|
embedded_thermostat_visual_min_temperature: "5"
|
|
embedded_thermostat_visual_max_temperature: "25"
|
|
embedded_thermostat_visual_temperature_step: "0.5"
|
|
|
|
|
|
###### USE THIS ONLY FOR YOUR FIRST TFT UPLOAD
|
|
###### AND IF EXIT-REPARSE BUTTON FAILS
|
|
###### ONCE IT WORKED, REMOVE THESE LINES
|
|
###### https://github.com/esphome/esphome/pull/2956
|
|
# external_components:
|
|
# - source: github://pr#2956
|
|
# components: [nextion]
|
|
# refresh: 1h
|
|
##################################################
|
|
|
|
##### if you rename this file to .nspanel_esphome.yaml then you can activate nested config
|
|
##### use the ADVANCED CONFIG-Section as device configuration
|
|
##### https://esphome.io/guides/configuration-types.html#yaml-insertion-operator
|
|
# <<: !include .nspanel_esphome.yaml
|
|
|
|
##### END OF ADVANCED CONFIGURATION ##############################################################
|
|
|
|
##### WIFI SETUP #####
|
|
wifi:
|
|
ssid: ${wifi_ssid}
|
|
password: ${wifi_password}
|
|
power_save_mode: none
|
|
##### advanced config - uncomment to use static IP-Config #####
|
|
# manual_ip:
|
|
# static_ip: ${ip}
|
|
# gateway: ${gw}
|
|
# subnet: ${subnet}
|
|
# dns1: ${dns}
|
|
# domain: ${domain}
|
|
##### Enable fallback hotspot (captive portal) in case wifi connection fails
|
|
ap:
|
|
ssid: "${device_name}"
|
|
password: ${wifi_password}
|
|
|
|
##### ESPHOME CONFIGURATION #####
|
|
esphome:
|
|
name: ${device_name}
|
|
min_version: 2023.5.0
|
|
|
|
##### TYPE OF ESP BOARD #####
|
|
esp32:
|
|
board: esp32dev
|
|
|
|
captive_portal:
|
|
|
|
web_server:
|
|
port: 80
|
|
auth:
|
|
username: admin
|
|
password: ${wifi_password}
|
|
##### advanced config - change to use web_password #####
|
|
# password: ${web_password}
|
|
|
|
##### OTA PASSWORD #####
|
|
ota:
|
|
password: ${wifi_password}
|
|
##### advanced config - change to use ota_password #####
|
|
# password: ${ota_password}
|
|
safe_mode: true
|
|
reboot_timeout: 3min
|
|
num_attempts: 3
|
|
|
|
##### LOGGER #####
|
|
logger:
|
|
baud_rate: 0
|
|
# level: WARN
|
|
|
|
##### CONFIGURE INTERNAL BUZZER #####
|
|
output:
|
|
##### BUZZER FOR PLAYING RINGTONES #####
|
|
- platform: ledc
|
|
id: buzzer_out
|
|
pin:
|
|
number: 21
|
|
|
|
##### ENABLE RINGTONE MUSIC SUPPORT #####
|
|
rtttl:
|
|
id: buzzer
|
|
output: buzzer_out
|
|
|
|
##### UART FOR NEXTION DISPLAY #####
|
|
uart:
|
|
tx_pin: 16
|
|
rx_pin: 17
|
|
baud_rate: ${baud_rate}
|
|
id: tf_uart
|
|
# debug:
|
|
# direction: BOTH
|
|
# dummy_receiver: false
|
|
# after:
|
|
# delimiter: "\n"
|
|
# sequence:
|
|
# - lambda: UARTDebug::log_string(direction, bytes);
|
|
|
|
### Keeps time display updated
|
|
time:
|
|
- platform: ${time_source}
|
|
id: time_provider
|
|
on_time:
|
|
- seconds: 0
|
|
then:
|
|
- script.execute:
|
|
id: refresh_datetime
|
|
on_time_sync:
|
|
then:
|
|
- logger.log: "Synchronized system clock"
|
|
- script.execute:
|
|
id: refresh_datetime
|
|
|
|
##### START - BUTTON CONFIGURATION #####
|
|
button:
|
|
###### REBOOT BUTTON #####
|
|
- name: ${device_name} Restart
|
|
platform: restart
|
|
id: restart_nspanel
|
|
|
|
##### UPDATE TFT DISPLAY #####
|
|
- name: ${device_name} Update TFT display
|
|
platform: template
|
|
icon: mdi:file-sync
|
|
id: tft_update
|
|
entity_category: config
|
|
on_press:
|
|
- logger.log: "Button pressed: Update TFT display"
|
|
- binary_sensor.template.publish:
|
|
id: nextion_init
|
|
state: false
|
|
- delay: 16ms
|
|
- lambda: id(disp1).upload_tft();
|
|
|
|
##### EXIT REPARSE TFT DISPLAY #####
|
|
- name: ${device_name} Exit reparse
|
|
platform: template
|
|
icon: mdi:file-sync
|
|
id: tft_reparse_off
|
|
entity_category: config
|
|
on_press:
|
|
- logger.log: "Button pressed: Exit reparse"
|
|
- uart.write:
|
|
id: tf_uart
|
|
data: "DRAKJHSUYDGBNCJHGJKSHBDN"
|
|
- uart.write:
|
|
id: tf_uart
|
|
data: [0xFF, 0xFF, 0xFF]
|
|
|
|
##### START - API CONFIGURATION #####
|
|
api:
|
|
id: api_server
|
|
##### advanced config - activate to use api_password #####
|
|
# password: ${api_password}
|
|
services:
|
|
|
|
##### SERVICE TO UPDATE THE HMI FILE ##############
|
|
- service: upload_tft
|
|
then:
|
|
- logger.log: "Service: upload_tft"
|
|
- binary_sensor.template.publish:
|
|
id: nextion_init
|
|
state: false
|
|
- lambda: 'id(disp1)->upload_tft();'
|
|
|
|
##### SERVICE TO UPDATE THE TFT FILE from URL #####
|
|
- service: upload_tft_url
|
|
variables:
|
|
url: string
|
|
then:
|
|
- logger.log: "Service: upload_tft_url"
|
|
- binary_sensor.template.publish:
|
|
id: nextion_init
|
|
state: false
|
|
- lambda: 'id(disp1)->set_tft_url(url.c_str());'
|
|
- lambda: 'id(disp1)->upload_tft();'
|
|
|
|
##### Service to send a command "printf" directly to the display #####
|
|
- service: send_command_printf
|
|
variables:
|
|
cmd: string
|
|
then:
|
|
- lambda: 'id(disp1).send_command_printf("%s", cmd.c_str());'
|
|
|
|
##### Service to send a command "text_printf" directly to the display #####
|
|
- service: send_command_text_printf
|
|
variables:
|
|
component: string
|
|
message: string
|
|
then:
|
|
- wait_until:
|
|
binary_sensor.is_on: nextion_init
|
|
- lambda: 'id(disp1).set_component_text_printf(component.c_str(), "%s", message.c_str());'
|
|
|
|
##### Service to send a command "component_value (Dualstate Button)" directly to the display #####
|
|
- service: send_command_value
|
|
variables:
|
|
component: string
|
|
message: int
|
|
then:
|
|
- wait_until:
|
|
binary_sensor.is_on: nextion_init
|
|
- lambda: 'id(disp1).set_component_value(component.c_str(), message);'
|
|
|
|
##### Service to send a command "hide componente" directly to the display #####
|
|
- service: send_command_hide ### unused ###
|
|
variables:
|
|
component: string
|
|
then:
|
|
- wait_until:
|
|
binary_sensor.is_on: nextion_init
|
|
- lambda: 'id(disp1).hide_component(component.c_str());'
|
|
|
|
##### Service to send a command "show componente" directly to the display #####
|
|
- service: send_command_show ### unused ###
|
|
variables:
|
|
component: string
|
|
then:
|
|
- wait_until:
|
|
binary_sensor.is_on: nextion_init
|
|
- lambda: 'id(disp1).show_component(component.c_str());'
|
|
|
|
##### Service to send a command "show ALL componente" directly to the display #####
|
|
- service: send_command_show_all ### unused ###
|
|
then:
|
|
- wait_until:
|
|
binary_sensor.is_on: nextion_init
|
|
- lambda: 'id(disp1).show_component("255");'
|
|
|
|
##### Service to send a command "font color" directly to the display #####
|
|
- service: send_command_font_color
|
|
variables:
|
|
component: string
|
|
message: int
|
|
then:
|
|
- wait_until:
|
|
binary_sensor.is_on: nextion_init
|
|
- lambda: 'id(disp1).set_component_font_color(component.c_str(), message);'
|
|
|
|
##### Service to send a command "background color" directly to the display #####
|
|
- service: send_command_background_color
|
|
variables:
|
|
component: string
|
|
message: int
|
|
then:
|
|
- wait_until:
|
|
binary_sensor.is_on: nextion_init
|
|
- lambda: 'id(disp1).set_component_background_color(component.c_str(), message);'
|
|
|
|
##### Service to show a notification-message on the screen #####
|
|
- service: notification_show
|
|
variables:
|
|
label: string
|
|
text: string
|
|
then:
|
|
- logger.log: "Service: notification_show"
|
|
- wait_until:
|
|
binary_sensor.is_on: nextion_init
|
|
- lambda: |-
|
|
id(disp1).send_command_printf("page notification");
|
|
id(disp1).set_component_text_printf("notification.notifi_label", "%s", label.c_str());
|
|
id(disp1).set_component_text_printf("notification.notifi_text01", "%s", text.c_str());
|
|
id(notification_label).publish_state(label.c_str());
|
|
id(notification_text).publish_state(text.c_str());
|
|
- switch.turn_on: notification_unread
|
|
- if:
|
|
condition:
|
|
switch.is_on: notification_sound
|
|
then:
|
|
- rtttl.play: "two short:d=4,o=5,b=100:16e6,16e6"
|
|
|
|
##### Service to clear the notification #####
|
|
- service: notification_clear
|
|
then:
|
|
- logger.log: "Service: notification_clear"
|
|
- wait_until:
|
|
binary_sensor.is_on: nextion_init
|
|
- lambda: |-
|
|
id(notification_label).publish_state("");
|
|
id(notification_text).publish_state("");
|
|
- switch.turn_off: notification_unread
|
|
|
|
##### Service to open information for settings-page(s)
|
|
- service: open_entity_settings_page
|
|
variables:
|
|
page: string
|
|
page_label: string
|
|
page_icon: string
|
|
page_icon_color: int
|
|
entity: string
|
|
back_page: string
|
|
then:
|
|
- wait_until:
|
|
binary_sensor.is_on: nextion_init
|
|
- lambda: |-
|
|
id(disp1).set_component_text_printf("home.entity", "%s", entity.c_str());
|
|
std::string cmd_page = std::string("page ") + page.c_str();
|
|
id(disp1).send_command_printf(cmd_page.c_str());
|
|
id(disp1).set_component_text_printf("page_label", "%s", page_label.c_str());
|
|
id(disp1).set_component_text_printf("back_page", "%s", back_page.c_str());
|
|
if ((page_icon.c_str() != std::string()) and (page_icon.c_str() != "") and (page != "climate"))
|
|
{
|
|
id(disp1).set_component_text_printf("icon_state", "%s", page_icon.c_str());
|
|
}
|
|
if (page_icon_color >= 0 and page != "climate")
|
|
{
|
|
id(disp1).set_component_font_color("icon_state", page_icon_color);
|
|
}
|
|
|
|
##### Service to play a rtttl tones #####
|
|
# Example tones : https://codebender.cc/sketch:109888#RTTTL%20Songs.ino
|
|
- service: play_rtttl
|
|
variables:
|
|
song_str: string
|
|
then:
|
|
- rtttl.play:
|
|
rtttl: !lambda 'return song_str;'
|
|
|
|
# Service to show a QR code on the display (ex. for WiFi password)
|
|
- service: qr_code
|
|
variables:
|
|
qrdata: string
|
|
then:
|
|
- wait_until:
|
|
binary_sensor.is_on: nextion_init
|
|
- lambda: |-
|
|
id(disp1).send_command_printf("page qrcode");
|
|
id(disp1).set_component_text_printf("qrcode.qrcode_value", "%s", qrdata.c_str());
|
|
|
|
#### Service to set climate state ####
|
|
- service: set_climate
|
|
variables:
|
|
current_temp: float
|
|
target_temp: float
|
|
temp_step: int
|
|
total_steps: int
|
|
temp_offset: int
|
|
climate_icon: string
|
|
embedded_climate: bool
|
|
then:
|
|
- lambda: |-
|
|
if (${verbose_log})
|
|
{
|
|
ESP_LOGD("api.service.set_climate", "climateslider.maxval=%i", total_steps);
|
|
ESP_LOGD("api.service.set_climate", "temp_offset=%f", temp_offset);
|
|
ESP_LOGD("api.service.set_climate", "temp_step=%f", temp_step);
|
|
ESP_LOGD("api.service.set_climate", "current_temp=%f", current_temp);
|
|
ESP_LOGD("api.service.set_climate", "target_temp=%f", target_temp);
|
|
ESP_LOGD("api.service.set_climate", "target_icon=%s", climate_icon.c_str());
|
|
ESP_LOGD("api.service.set_climate", "embedded=%i", (embedded_climate) ? 1 : 0);
|
|
}
|
|
|
|
- script.execute:
|
|
id: set_climate
|
|
current_temp: !lambda "return current_temp;"
|
|
target_temp: !lambda "return target_temp;"
|
|
temp_step: !lambda "return temp_step;"
|
|
total_steps: !lambda "return total_steps;"
|
|
temp_offset: !lambda "return temp_offset;"
|
|
climate_icon: !lambda "return climate_icon;"
|
|
embedded_climate: !lambda "return embedded_climate;"
|
|
|
|
#### Service to set the buttons ####
|
|
- service: set_button
|
|
variables:
|
|
btn_id: string
|
|
btn_pic: int
|
|
btn_bg: int
|
|
btn_icon_font: int
|
|
btn_txt_font: int
|
|
btn_bri_font: int
|
|
btn_icon: string
|
|
btn_label: string
|
|
btn_bri_txt: string
|
|
then:
|
|
- wait_until:
|
|
binary_sensor.is_on: nextion_init
|
|
- lambda: |-
|
|
// ESP_LOGD("nextion", "set button %s", btn_id.c_str());
|
|
std::string btnicon = btn_id.c_str() + std::string("icon");
|
|
std::string btntext = btn_id.c_str() + std::string("text");
|
|
std::string btnbri = btn_id.c_str() + std::string("bri");
|
|
id(disp1).send_command_printf("%spic.pic=%i", btn_id.c_str(), btn_pic);
|
|
id(disp1).set_component_background_color(btnicon.c_str(), btn_bg);
|
|
id(disp1).set_component_background_color(btntext.c_str(), btn_bg);
|
|
id(disp1).set_component_background_color(btnbri.c_str(), btn_bg);
|
|
id(disp1).set_component_font_color(btnicon.c_str(), btn_icon_font);
|
|
id(disp1).set_component_font_color(btntext.c_str(), btn_txt_font);
|
|
id(disp1).set_component_font_color(btnbri.c_str(), btn_bri_font);
|
|
id(disp1).set_component_text_printf(btnicon.c_str(), "%s", btn_icon.c_str());
|
|
id(disp1).set_component_text_printf(btntext.c_str(), "%s", btn_label.c_str());
|
|
// id(disp1).set_component_text_printf(btnbri.c_str(), "%s", btn_bri_txt.c_str());
|
|
if (strcmp(btn_bri_txt.c_str(), "0") != 0) {
|
|
id(disp1).set_component_text_printf(btnbri.c_str(), "%s", btn_bri_txt.c_str());
|
|
} else {
|
|
id(disp1).set_component_text_printf(btnbri.c_str(), " ");
|
|
}
|
|
|
|
##### SERVICE TO WAKE UP THE DISPLAY #####
|
|
- service: wake_up_display
|
|
variables:
|
|
option: string
|
|
then:
|
|
- lambda: |-
|
|
DynamicJsonDocument doc(1024);
|
|
deserializeJson(doc, id(disp1_nspanel_event).state);
|
|
std::string page = doc["page"];
|
|
if (page == "screensaver") {
|
|
id(disp1).send_command_printf("page home");
|
|
} else {
|
|
if (page == "home"){
|
|
id(disp1).send_command_printf("dim=brightness.val");
|
|
}
|
|
}
|
|
- if:
|
|
condition:
|
|
- lambda: 'return option == "keep_wake";'
|
|
then:
|
|
- lambda: id(disp1).send_command_printf("home.dimtimer.en=1");
|
|
- lambda: id(disp1).send_command_printf("home.sleeptimer.en=1");
|
|
- if:
|
|
condition:
|
|
- lambda: 'return option == "keep_page";'
|
|
then:
|
|
- lambda: id(disp1).send_command_printf("home.dimtimer.en=1");
|
|
- lambda: id(disp1).send_command_printf("home.sleeptimer.en=1");
|
|
- lambda: |-
|
|
id(page_timer)->execute(int(id(page_timeout).state));
|
|
|
|
#### Service to set the entities ####
|
|
- service: set_entity
|
|
variables:
|
|
ent_id: string
|
|
ent_icon: string
|
|
ent_label: string
|
|
ent_value: string
|
|
ent_value_xcen: string
|
|
then:
|
|
- wait_until:
|
|
binary_sensor.is_on: nextion_init
|
|
- lambda: |-
|
|
// ESP_LOGD("nextion", "set entity %s", ent_id.c_str());
|
|
std::string enticon = ent_id.c_str() + std::string("_pic");
|
|
std::string entlabel = ent_id.c_str() + std::string("_label");
|
|
std::string entxcen = ent_id.c_str() + std::string(".xcen=") + ent_value_xcen.c_str();
|
|
id(disp1).set_component_text_printf(enticon.c_str(), "%s", ent_icon.c_str());
|
|
if (strcmp(ent_icon.c_str(), "0") != 0) {
|
|
id(disp1).set_component_text_printf(enticon.c_str(), "%s", ent_icon.c_str());
|
|
}
|
|
id(disp1).set_component_text_printf(entlabel.c_str(), "%s", ent_label.c_str());
|
|
id(disp1).set_component_text_printf(ent_id.c_str(), "%s", ent_value.c_str());
|
|
if (strcmp(ent_value_xcen.c_str(), "0") != 0) {
|
|
id(disp1).send_command_printf("%s", entxcen.c_str());
|
|
}
|
|
|
|
##### Service for transferring global settings from the blueprint to ESPHome #####
|
|
- service: global_settings
|
|
variables:
|
|
relay1_local_control: bool
|
|
relay1_icon: string
|
|
relay1_icon_color: int
|
|
relay2_local_control: bool
|
|
relay2_icon: string
|
|
relay2_icon_color: int
|
|
date_color: int
|
|
time_format: string
|
|
time_color: int
|
|
embedded_climate: bool
|
|
wakeup_page: int
|
|
then:
|
|
## Logs
|
|
- lambda: |-
|
|
if (${verbose_log})
|
|
{
|
|
ESP_LOGD("global_settings", "relay1_local_control: %i", (relay1_local_control) ? 1 : 0);
|
|
ESP_LOGD("global_settings", "relay2_local_control: %i", (relay2_local_control) ? 1 : 0);
|
|
ESP_LOGD("global_settings", "date_color: %i", date_color);
|
|
ESP_LOGD("global_settings", "time_format: %s", time_format.c_str());
|
|
ESP_LOGD("global_settings", "time_color: %i", time_color);
|
|
ESP_LOGD("global_settings", "embedded_climate: %i", (embedded_climate and not (${embedded_thermostat_disabled})) ? 1 : 0);
|
|
}
|
|
|
|
## Relays
|
|
- lambda: |-
|
|
id(relay1_local).publish_state(relay1_local_control);
|
|
id(relay2_local).publish_state(relay2_local_control);
|
|
id(home_relay1_icon) = relay1_icon.c_str();
|
|
id(home_relay2_icon) = relay2_icon.c_str();
|
|
id(home_relay1_icon_color) = relay1_icon_color;
|
|
id(home_relay2_icon_color) = relay2_icon_color;
|
|
|
|
## Localization
|
|
- lambda: id(mui_time_format) = time_format;
|
|
|
|
## Date/Time colors
|
|
- lambda: |-
|
|
id(home_date_color) = date_color;
|
|
id(home_time_color) = time_color;
|
|
|
|
## Embedded thermostat
|
|
- lambda: id(is_embedded_thermostat) = (embedded_climate and not (${embedded_thermostat_disabled}));
|
|
|
|
## Wakeup page
|
|
- lambda: id(wakeup_page_id) = wakeup_page;
|
|
|
|
## Refresh colors of global components
|
|
- script.execute:
|
|
id: refresh_colors
|
|
|
|
## Update home page
|
|
- script.execute:
|
|
id: update_page_home
|
|
|
|
##### START - GLOBALS CONFIGURATION #####
|
|
globals:
|
|
|
|
##### Save Display Brightness for NSPanel reboot #####
|
|
- id: display_brightness_global
|
|
type: int
|
|
restore_value: true
|
|
initial_value: '100'
|
|
|
|
##### Save Display DIM Brightness for NSPanel reboot
|
|
- id: display_dim_brightness_global
|
|
type: int
|
|
restore_value: true
|
|
initial_value: '10'
|
|
|
|
##### Temperature Correction #####
|
|
- id: temperature_correction_global
|
|
type: float
|
|
restore_value: true
|
|
initial_value: '0.0'
|
|
|
|
##### Date/time formats #####
|
|
#- id: mui_date_format
|
|
# type: std::string
|
|
# restore_value: no
|
|
# initial_value: '"%A, %d.%m"'
|
|
- id: home_date_color
|
|
type: int
|
|
restore_value: true
|
|
initial_value: '65535'
|
|
|
|
- id: mui_time_format
|
|
type: std::string
|
|
restore_value: no
|
|
initial_value: '"%H:%M"'
|
|
|
|
- id: home_time_color
|
|
type: int
|
|
restore_value: true
|
|
initial_value: '65535'
|
|
|
|
##### Is embedded thermostat set as main climate entity? #####
|
|
- id: is_embedded_thermostat
|
|
type: bool
|
|
restore_value: true
|
|
initial_value: 'false'
|
|
##### Is embedded thermostat visible on climate page? #####
|
|
- id: is_embedded_thermostat_visible
|
|
type: bool
|
|
restore_value: false
|
|
initial_value: 'false'
|
|
|
|
##### Relay icons #####
|
|
- id: home_relay1_icon
|
|
type: std::string
|
|
restore_value: false
|
|
initial_value: ''
|
|
|
|
- id: home_relay1_icon_color
|
|
type: int
|
|
restore_value: true
|
|
initial_value: '65535'
|
|
|
|
- id: home_relay2_icon
|
|
type: std::string
|
|
restore_value: false
|
|
initial_value: ''
|
|
|
|
- id: home_relay2_icon_color
|
|
type: int
|
|
restore_value: true
|
|
initial_value: '65535'
|
|
|
|
##### Wakeup page ID #####
|
|
- id: wakeup_page_id
|
|
type: int
|
|
restore_value: true
|
|
initial_value: '0'
|
|
|
|
##### START - BINARY SENSOR CONFIGURATION #####
|
|
binary_sensor:
|
|
|
|
###### LEFT BUTTON BELOW DISPLAY TO TOGGLE RELAY#####
|
|
- name: ${device_name} Left Button
|
|
platform: gpio
|
|
id: left_button
|
|
pin:
|
|
number: 14
|
|
inverted: true
|
|
on_click:
|
|
then:
|
|
- if:
|
|
condition:
|
|
or:
|
|
- switch.is_on: relay1_local
|
|
- and:
|
|
- switch.is_on: relay1_fallback
|
|
- not:
|
|
api.connected:
|
|
then:
|
|
- switch.toggle: relay_1
|
|
- script.execute:
|
|
id: refresh_relays
|
|
|
|
##### RIGHT BUTTON BELOW DISPLAY TO TOGGLE RELAY #####
|
|
- name: ${device_name} Right Button
|
|
platform: gpio
|
|
id: right_button
|
|
pin:
|
|
number: 27
|
|
inverted: true
|
|
on_click:
|
|
then:
|
|
- if:
|
|
condition:
|
|
or:
|
|
- switch.is_on: relay2_local
|
|
- and:
|
|
- switch.is_on: relay2_fallback
|
|
- not:
|
|
api.connected:
|
|
then:
|
|
- switch.toggle: relay_2
|
|
- script.execute:
|
|
id: refresh_relays
|
|
|
|
##### Restart NSPanel Button - Setting Page #####
|
|
- name: ${device_name} Restart
|
|
platform: nextion
|
|
page_id: 7
|
|
component_id: 13
|
|
internal: true
|
|
on_click:
|
|
- button.press: restart_nspanel
|
|
|
|
##### Restart NSPanel Button - Boot Page #####
|
|
- name: ${device_name} Restart
|
|
platform: nextion
|
|
page_id: 8
|
|
component_id: 4
|
|
internal: true
|
|
on_click:
|
|
- button.press: restart_nspanel
|
|
|
|
##### Sleep mode NSPanel Button #####
|
|
- name: ${device_name} Sleep mode
|
|
platform: nextion
|
|
page_id: 7
|
|
component_id: 14
|
|
internal: true
|
|
on_click:
|
|
- logger.log: "Sleep mode - Nextion toggle"
|
|
- switch.toggle: sleep_mode
|
|
|
|
##### global variable to keep track on whether the Nextion display is ready or not.
|
|
## Delays initial info from HA to the display #####
|
|
- name: ${device_name} Nextion display
|
|
id: nextion_init
|
|
platform: template
|
|
device_class: connectivity
|
|
publish_initial_state: true
|
|
entity_category: diagnostic
|
|
icon: mdi:tablet-dashboard
|
|
|
|
##### API connection status
|
|
- platform: status
|
|
name: ${device_name} Status
|
|
id: api_status
|
|
on_state:
|
|
then:
|
|
- script.execute:
|
|
id: refresh_wifi_icon
|
|
|
|
##### START - SENSOR CONFIGURATION #####
|
|
sensor:
|
|
|
|
##### Uptime #####
|
|
- name: ${device_name} uptime
|
|
platform: uptime
|
|
disabled_by_default: true
|
|
|
|
##### WIFI Signal stregth
|
|
- name: ${device_name} RSSI
|
|
platform: wifi_signal
|
|
update_interval: 60s
|
|
on_value:
|
|
- script.execute:
|
|
id: refresh_wifi_icon
|
|
|
|
##### INTERNAL TEMPERATURE SENSOR, ADC VALUE #####
|
|
- id: ntc_source
|
|
platform: adc
|
|
pin: 38
|
|
update_interval: 60s
|
|
attenuation: 11db
|
|
|
|
##### INTERNAL TEMPERATURE SENSOR, adc reading converted to resistance (calculation)#####
|
|
- id: resistance_sensor
|
|
platform: resistance
|
|
sensor: ntc_source
|
|
configuration: DOWNSTREAM
|
|
resistor: 11.2kOhm
|
|
|
|
##### INTERNAL TEMPERATURE SENSOR, resistance to temperature (calculation) #####
|
|
- name: ${device_name} Temperature
|
|
platform: ntc
|
|
id: temp_nspanel
|
|
sensor: resistance_sensor
|
|
calibration:
|
|
b_constant: 3950
|
|
reference_temperature: 25°C
|
|
reference_resistance: 10kOhm
|
|
filters:
|
|
- lambda: return x + id(temperature_correction_global);
|
|
#on_value:
|
|
# then:
|
|
# - wait_until:
|
|
# binary_sensor.is_on: nextion_init
|
|
# - lambda: id(disp1).set_component_text_printf("home.current_temp", "%.1f°", id(temp_nspanel).state); # onboard temp (thermostat temp) to home page.
|
|
# - lambda: id(disp1).set_component_text_printf("climate.current_temp", "%.1f", id(temp_nspanel).state);
|
|
|
|
###### Display Brightness GET VALUE FROM NSPanel SLIDER #####
|
|
- name: ${device_name} brightness Slider
|
|
platform: nextion
|
|
id: brightslider
|
|
variable_name: brightslider
|
|
internal: true
|
|
on_value:
|
|
then:
|
|
- wait_until:
|
|
binary_sensor.is_on: nextion_init
|
|
- number.set:
|
|
id: display_brightness
|
|
value: !lambda 'return int(x);'
|
|
# send text field percentage of current_lightslider_val
|
|
- lambda: id(disp1).set_component_text_printf("settings.a03", "%i", id(display_brightness_global));
|
|
|
|
###### Display DIM Brightness GET VALUE FROM NSPanel SLIDER #####
|
|
- name: ${device_name} dim brightness slider
|
|
platform: nextion
|
|
id: dimslider
|
|
variable_name: dimslider
|
|
internal: true
|
|
on_value:
|
|
then:
|
|
- wait_until:
|
|
binary_sensor.is_on: nextion_init
|
|
- number.set:
|
|
id: display_dim_brightness
|
|
value: !lambda 'return int(x);'
|
|
# send text field percentage of current_lightslider_val
|
|
- lambda: id(disp1).set_component_text_printf("settings.a04", "%i", id(display_dim_brightness_global));
|
|
|
|
##### START - TEXT SENSOR CONFIGURATION #####
|
|
text_sensor:
|
|
|
|
##### Current page name #####
|
|
- name: ${device_name} current page
|
|
platform: template
|
|
id: current_page
|
|
internal: false
|
|
disabled_by_default: true
|
|
|
|
##### ESPhome version used to compile the app #####
|
|
- platform: version
|
|
name: ${device_name} ESPhome Version
|
|
disabled_by_default: true
|
|
|
|
- platform: wifi_info
|
|
ip_address:
|
|
name: ${device_name} IP
|
|
disabled_by_default: true
|
|
id: ip_address
|
|
ssid:
|
|
name: ${device_name} SSID
|
|
disabled_by_default: true
|
|
bssid:
|
|
name: ${device_name} BSSID
|
|
disabled_by_default: true
|
|
|
|
- name: ${device_name} Notification Label
|
|
platform: template
|
|
id: notification_label
|
|
|
|
- name: ${device_name} Notification Text
|
|
platform: template
|
|
id: notification_text
|
|
|
|
##### NSPanel event sensor, the main action sensor - push to HA #####
|
|
- name: ${device_name} NSPanel event
|
|
platform: nextion
|
|
nextion_id: disp1
|
|
id: disp1_nspanel_event
|
|
component_name: nspanelevent
|
|
internal: false
|
|
filters:
|
|
- lambda: |-
|
|
x = x.c_str();
|
|
x.shrink_to_fit();
|
|
return x;
|
|
on_value:
|
|
then:
|
|
- lambda: |-
|
|
id(page_timer)->execute(int(id(page_timeout).state));
|
|
DynamicJsonDocument doc(1024);
|
|
deserializeJson(doc, x);
|
|
std::string page = doc["page"];
|
|
std::string component = doc["component"];
|
|
std::string value = doc["value"];
|
|
std::string entity = doc["entity"];
|
|
if (component=="currentpage")
|
|
{
|
|
ESP_LOGD("nspanelevent", "New page: %s", page.c_str());
|
|
auto ha_event = new esphome::api::CustomAPIDevice();
|
|
ha_event->fire_homeassistant_event("esphome.nspanel_pagechange",
|
|
{
|
|
{"page", page},
|
|
{"entity", entity}
|
|
});
|
|
id(current_page).publish_state(page);
|
|
if (page=="home") id(update_page_home).execute();
|
|
else if (page=="screensaver") id(disp1).set_component_value("orign", id(wakeup_page_id));
|
|
else if (page=="climate" and id(is_embedded_thermostat_visible)) id(update_page_climate);
|
|
}
|
|
else if (page=="boot" and component=="timeout" and stof(value) >= 5) id(disp1).send_command_printf("page %i", id(wakeup_page_id));
|
|
|
|
##### NSPanel event - Execute actions from ESPHome - NO push to HA #####
|
|
- name: ${device_name} NSPanel local event
|
|
platform: nextion
|
|
nextion_id: disp1
|
|
id: disp1_local_event
|
|
component_name: localevent
|
|
internal: True
|
|
filters:
|
|
- lambda: |-
|
|
x = x.c_str();
|
|
x.shrink_to_fit();
|
|
return x;
|
|
on_value:
|
|
then:
|
|
- lambda: |-
|
|
id(page_timer)->execute(int(id(page_timeout).state));
|
|
DynamicJsonDocument doc(1024);
|
|
deserializeJson(doc, x);
|
|
std::string domain = doc["domain"];
|
|
std::string key = doc["key"];
|
|
std::string value = doc["value"];
|
|
std::string entity = doc["entity"];
|
|
int embedded = doc["embedded"];
|
|
std::string service = "";
|
|
if (${verbose_log})
|
|
{
|
|
ESP_LOGD("text_sensor.localevent", "domain=%s", domain.c_str());
|
|
ESP_LOGD("text_sensor.localevent", "key=%s", key.c_str());
|
|
ESP_LOGD("text_sensor.localevent", "value=%s", value.c_str());
|
|
ESP_LOGD("text_sensor.localevent", "entity=%s", entity.c_str());
|
|
ESP_LOGD("text_sensor.localevent", "embedded=%i", embedded);
|
|
}
|
|
id(is_embedded_thermostat_visible) = (domain == "climate" and embedded == 1);
|
|
if (id(is_embedded_thermostat_visible))
|
|
{
|
|
if (${verbose_log}) ESP_LOGD("text_sensor.localevent", "Embedded thermostat is visible");
|
|
auto call = id(thermostat_embedded).make_call();
|
|
if (key == "set_temperature")
|
|
{
|
|
if (${verbose_log}) ESP_LOGD("text_sensor.localevent", "set_temperature=%f", stof(value) / 10);
|
|
call.set_target_temperature(stof(value) / 10);
|
|
}
|
|
else if (key == "hvac_mode")
|
|
{
|
|
if (${verbose_log}) ESP_LOGD("text_sensor.localevent", "set_mode=%s", value);
|
|
call.set_mode(value);
|
|
}
|
|
call.perform();
|
|
}
|
|
else if (entity != "" and not entity.empty() and entity != "embedded_climate")
|
|
{
|
|
if (domain == "alarm_control_panel")
|
|
{
|
|
if (${verbose_log}) ESP_LOGD("text_sensor.localevent", "ESPHome remote service call - Alarm control panel");
|
|
HomeassistantServiceResponse resp;
|
|
HomeassistantServiceMap resp_kv;
|
|
resp.service = "alarm_control_panel.XXXX"; // DEBUG
|
|
resp_kv.key = "entity_id";
|
|
resp_kv.value = entity;
|
|
resp.data.push_back(resp_kv);
|
|
resp_kv.key = "pin"; // DEBUG
|
|
resp_kv.value = value;
|
|
resp.data.push_back(resp_kv);
|
|
id(api_server).send_homeassistant_service_call(resp);
|
|
}
|
|
else
|
|
{
|
|
if (${verbose_log}) ESP_LOGD("text_sensor.localevent", "Blueprint controlled service");
|
|
auto ha_event = new esphome::api::CustomAPIDevice();
|
|
if (domain == "climate")
|
|
{
|
|
|
|
if (key == "set_temperature")
|
|
{
|
|
service = "climate.set_temperature";
|
|
value = to_string(stof(value) / 10);
|
|
}
|
|
else if (key == "hvac_mode")
|
|
{
|
|
service = "climate.set_hvac_mode";
|
|
}
|
|
}
|
|
else if (domain == "cover")
|
|
{
|
|
if (key == "position")
|
|
{
|
|
service = "cover.set_cover_position";
|
|
}
|
|
else
|
|
{
|
|
service = std::string("cover.") + key.c_str();
|
|
key = "";
|
|
value = "";
|
|
}
|
|
}
|
|
else if (domain == "fan")
|
|
{
|
|
if (key == "stop")
|
|
{
|
|
service = "fan.turn_off";
|
|
key = "";
|
|
value = "";
|
|
}
|
|
else
|
|
{
|
|
service = "fan.turn_on";
|
|
}
|
|
}
|
|
else if (domain == "light")
|
|
{
|
|
service = "light.turn_on";
|
|
}
|
|
if (${verbose_log}) ESP_LOGD("text_sensor.localevent", "Service=%s", service.c_str());
|
|
if (service != "" and not service.empty())
|
|
ha_event->fire_homeassistant_event("esphome.nspanel_service_call",
|
|
{
|
|
{"service", service},
|
|
{"entity", entity},
|
|
{"key", key},
|
|
{"value", value}
|
|
});
|
|
}
|
|
}
|
|
|
|
##### touchevent sensor, Reset the page timeout #####
|
|
- id: disp1_touchevent
|
|
platform: nextion
|
|
nextion_id: disp1
|
|
#name: ${device_name} touchevent
|
|
component_name: touchevent
|
|
internal: true
|
|
filters:
|
|
- lambda: |-
|
|
x = x.c_str();
|
|
x.shrink_to_fit();
|
|
return x;
|
|
on_value:
|
|
then:
|
|
- lambda: |-
|
|
id(page_timer)->execute(int(id(page_timeout).state));
|
|
|
|
##### START - SWITCH CONFIGURATION #####
|
|
switch:
|
|
|
|
##### Notification unread #####
|
|
- name: ${device_name} Notification unread
|
|
platform: template
|
|
id: notification_unread
|
|
entity_category: config
|
|
restore_state: true
|
|
optimistic: true
|
|
|
|
##### Notification sound #####
|
|
- name: ${device_name} Notification sound
|
|
platform: template
|
|
id: notification_sound
|
|
entity_category: config
|
|
restore_state: true
|
|
optimistic: true
|
|
restore_mode: RESTORE_DEFAULT_OFF
|
|
|
|
##### Confirmation Message #####
|
|
- name: ${device_name} Confirmation Message
|
|
platform: template
|
|
id: confirmation_message
|
|
entity_category: config
|
|
restore_state: false
|
|
optimistic: true
|
|
|
|
##### PHYSICAL SWITCH 1 #####
|
|
- name: ${device_name} Relay 1
|
|
platform: gpio
|
|
id: relay_1
|
|
pin:
|
|
number: 22
|
|
restore_mode: RESTORE_DEFAULT_OFF
|
|
|
|
##### PHYSICAL SWITCH 2 ######
|
|
- name: ${device_name} Relay 2
|
|
platform: gpio
|
|
id: relay_2
|
|
pin:
|
|
number: 19
|
|
restore_mode: RESTORE_DEFAULT_OFF
|
|
|
|
##### DISPLAY ALWAYS ON #####
|
|
- name: ${device_name} Screen Power
|
|
platform: gpio
|
|
id: screen_power
|
|
entity_category: config
|
|
pin:
|
|
number: 4
|
|
inverted: true
|
|
restore_mode: ALWAYS_ON
|
|
internal: true
|
|
|
|
##### Switch Display Sleep mode #####
|
|
- name: ${device_name} Sleep mode
|
|
platform: template
|
|
device_class: switch
|
|
id: sleep_mode
|
|
entity_category: config
|
|
restore_mode: RESTORE_DEFAULT_OFF
|
|
restore_state: true
|
|
optimistic: false
|
|
turn_on_action: &sleep_mode-turn_on
|
|
- logger.log: "Sleep mode - Turn on"
|
|
- lambda: id(disp1).send_command_printf("home.sleepmodus.val=1");
|
|
- lambda: id(disp1).set_component_value("settings.bt1",1);
|
|
- switch.template.publish:
|
|
id: sleep_mode
|
|
state: ON
|
|
turn_off_action: &sleep_mode-turn_off
|
|
- logger.log: "Sleep mode - Turn off"
|
|
- lambda: id(disp1).send_command_printf("home.sleepmodus.val=0");
|
|
- lambda: id(disp1).set_component_value("settings.bt1",0);
|
|
- switch.template.publish:
|
|
id: sleep_mode
|
|
state: OFF
|
|
|
|
##### Relay Local control #####
|
|
- name: ${device_name} Relay 1 Local
|
|
platform: template
|
|
id: relay1_local
|
|
entity_category: config
|
|
restore_state: true
|
|
optimistic: true
|
|
restore_mode: RESTORE_DEFAULT_OFF
|
|
internal: true
|
|
on_turn_on:
|
|
- logger.log: "Relay 1 Local turned On!"
|
|
on_turn_off:
|
|
- logger.log: "Relay 1 Local turned Off!"
|
|
- name: ${device_name} Relay 2 Local
|
|
platform: template
|
|
id: relay2_local
|
|
entity_category: config
|
|
restore_state: true
|
|
optimistic: true
|
|
restore_mode: RESTORE_DEFAULT_OFF
|
|
internal: true
|
|
on_turn_on:
|
|
- logger.log: "Relay 2 Local turned On!"
|
|
on_turn_off:
|
|
- logger.log: "Relay 2 Local turned Off!"
|
|
|
|
##### Relay Local control Fallback #####
|
|
- name: ${device_name} Relay 1 Local Fallback
|
|
platform: template
|
|
id: relay1_fallback
|
|
entity_category: config
|
|
restore_state: true
|
|
optimistic: true
|
|
restore_mode: RESTORE_DEFAULT_OFF
|
|
- name: ${device_name} Relay 2 Local Fallback
|
|
platform: template
|
|
id: relay2_fallback
|
|
entity_category: config
|
|
restore_state: true
|
|
optimistic: true
|
|
restore_mode: RESTORE_DEFAULT_OFF
|
|
|
|
##### START - NUMBER CONFIGURATION #####
|
|
number:
|
|
|
|
##### SCREEN BRIGHTNESS #####
|
|
- platform: template
|
|
name: ${device_name} Display Brightness
|
|
id: display_brightness
|
|
entity_category: config
|
|
unit_of_measurement: '%'
|
|
min_value: 1
|
|
max_value: 100
|
|
step: 1
|
|
restore_value: true
|
|
optimistic: true
|
|
set_action:
|
|
then:
|
|
- lambda: 'id(disp1).set_backlight_brightness(x/100);'
|
|
- lambda: 'id(disp1).send_command_printf("home.brightness.val=%i", int(x));'
|
|
- globals.set:
|
|
id: display_brightness_global
|
|
value: !lambda 'return int(x);'
|
|
|
|
##### SCREEN BRIGHTNESS DIMMED DOWN #####
|
|
- platform: template
|
|
name: ${device_name} Display Brightness Dimdown
|
|
id: display_dim_brightness
|
|
entity_category: config
|
|
unit_of_measurement: '%'
|
|
min_value: 1
|
|
max_value: 100
|
|
step: 1
|
|
restore_value: true
|
|
optimistic: true
|
|
set_action:
|
|
then:
|
|
- lambda: 'id(disp1).send_command_printf("home.brightdd.val=%i", int(x));'
|
|
- globals.set:
|
|
id: display_dim_brightness_global
|
|
value: !lambda 'return int(x);'
|
|
|
|
##### Temperature Correction #####
|
|
- platform: template
|
|
name: ${device_name} Temperature Correction
|
|
id: temperature_correction
|
|
entity_category: config
|
|
unit_of_measurement: '°C'
|
|
min_value: -10
|
|
max_value: 10
|
|
step: 0.5
|
|
restore_value: true
|
|
optimistic: true
|
|
set_action:
|
|
then:
|
|
- globals.set:
|
|
id: temperature_correction_global
|
|
value: !lambda 'return x;'
|
|
|
|
##### page-timeout #####
|
|
- platform: template
|
|
name: ${device_name} Page Timeout
|
|
id: page_timeout
|
|
entity_category: config
|
|
min_value: 0
|
|
max_value: 60
|
|
initial_value: 10
|
|
step: 1
|
|
restore_value: true
|
|
optimistic: true
|
|
|
|
##### START - DISPLAY START CONFIGURATION #####
|
|
display:
|
|
- id: disp1
|
|
platform: nextion
|
|
uart_id: tf_uart
|
|
tft_url: ${nextion_update_url}
|
|
on_setup:
|
|
then:
|
|
- logger.log: "Nextion start - Jump to page 8"
|
|
- lambda: id(disp1).send_command_printf("page 8");
|
|
- logger.log: "Nextion start - Publish ESPHome version"
|
|
- lambda: id(disp1).set_component_text_printf("boot.esph_version", "%s", "3.5_dev"); ### esphome-version ###
|
|
- logger.log: "Nextion start - Wait for Home Assistant API"
|
|
- wait_until:
|
|
api.connected
|
|
- logger.log: "Nextion start - Publish IP address"
|
|
- lambda: id(disp1).set_component_text_printf("boot.ip_addr", "%s", id(ip_address).state.c_str());
|
|
- delay: 1s
|
|
- logger.log: "Nextion start - Set display brigntess"
|
|
- number.set:
|
|
id: display_brightness
|
|
value: !lambda 'return id(display_brightness_global);'
|
|
- logger.log: "Nextion start - Set display dim brightness"
|
|
- number.set:
|
|
id: display_dim_brightness
|
|
value: !lambda 'return id(display_dim_brightness_global);'
|
|
- logger.log: "Nextion start - Update settings page"
|
|
- lambda: id(disp1).set_component_text_printf("settings.a03", "%i", id(display_brightness_global));
|
|
- lambda: id(disp1).set_component_text_printf("settings.a04", "%i", id(display_dim_brightness_global));
|
|
- lambda: id(disp1).send_command_printf("settings.brightslider.val=%i", id(display_brightness_global));
|
|
- lambda: id(disp1).send_command_printf("settings.dimslider.val=%i", id(display_dim_brightness_global));
|
|
- if:
|
|
condition:
|
|
switch.is_off: sleep_mode
|
|
then: *sleep_mode-turn_off
|
|
else: *sleep_mode-turn_on
|
|
- delay: 1s
|
|
- logger.log: "Nextion start - Inform Home Assistant display is ready"
|
|
- binary_sensor.template.publish:
|
|
id: nextion_init
|
|
state: true
|
|
- logger.log: "Nextion start - Prepare home page"
|
|
- script.execute:
|
|
id: refresh_colors
|
|
- lambda: id(home_relay1_icon) = "\uE3A5";
|
|
- lambda: id(home_relay1_icon) = "\uE3A8";
|
|
- logger.log: "Nextion start - Done!"
|
|
#on_page: # Couldn't make this trigger to work, so used text_sensor nspanelevent and localevent instead
|
|
|
|
### Script for page_timer
|
|
script:
|
|
- id: page_timer
|
|
mode: restart
|
|
parameters:
|
|
delay: int
|
|
then:
|
|
- lambda: if (${verbose_log}) ESP_LOGD("script.page_timer", "start page-timer delay %i", int(id(page_timeout).state));
|
|
- delay: !lambda return delay *1000;
|
|
- lambda: |-
|
|
DynamicJsonDocument doc(1024);
|
|
deserializeJson(doc, id(disp1_nspanel_event).state);
|
|
std::string page = doc["page"];
|
|
if (page == "home" or page == "screensaver" or page == "boot" or int(id(page_timeout).state) == 0)
|
|
{
|
|
if (${verbose_log}) ESP_LOGD("script.page_timer", "no page-jump");
|
|
}
|
|
else
|
|
{
|
|
if (${verbose_log}) ESP_LOGD("script.page_timer", "timer->home");
|
|
id(disp1).send_command_printf("page 0");
|
|
}
|
|
|
|
- id: set_climate
|
|
mode: restart
|
|
parameters:
|
|
current_temp: float
|
|
target_temp: float
|
|
temp_step: int
|
|
total_steps: int
|
|
temp_offset: int
|
|
climate_icon: string
|
|
embedded_climate: bool
|
|
then:
|
|
- if:
|
|
condition:
|
|
- binary_sensor.is_on: nextion_init
|
|
- text_sensor.state: # Is climate page visible?
|
|
id: current_page
|
|
state: 'climate'
|
|
then:
|
|
- lambda: |-
|
|
if (${verbose_log})
|
|
{
|
|
ESP_LOGD("script.set_climate", "climateslider.maxval=%i", total_steps);
|
|
ESP_LOGD("script.set_climate", "temp_offset=%i", temp_offset);
|
|
ESP_LOGD("script.set_climate", "temp_step=%i", temp_step);
|
|
ESP_LOGD("script.set_climate", "current_temp=%f", current_temp);
|
|
ESP_LOGD("script.set_climate", "target_temp=%f", target_temp);
|
|
ESP_LOGD("script.set_climate", "target_icon=%s", climate_icon.c_str());
|
|
ESP_LOGD("script.set_climate", "embedded=%i", (embedded_climate) ? 1 : 0);
|
|
}
|
|
id(is_embedded_thermostat_visible) = embedded_climate;
|
|
id(disp1).send_command_printf("climateslider.maxval=%i", total_steps);
|
|
id(disp1).set_component_value("temp_offset", temp_offset);
|
|
id(disp1).set_component_value("temp_step", temp_step);
|
|
id(disp1).set_component_text_printf("current_temp", "%.1f°", current_temp);
|
|
id(disp1).show_component("current_temp");
|
|
id(disp1).show_component("current_icon");
|
|
if (target_temp > -999)
|
|
{
|
|
float slider_val = round(((10*target_temp) - temp_offset) / temp_step);
|
|
if (${verbose_log}) ESP_LOGD("script.set_climate", "climateslider=%f", slider_val);
|
|
id(disp1).set_component_value("climateslider", slider_val);
|
|
id(disp1).set_component_text_printf("target_temp", "%.1f°", target_temp);
|
|
id(disp1).set_component_text_printf("target_icon", "%s", climate_icon.c_str());
|
|
id(disp1).show_component("target_icon");
|
|
id(disp1).show_component("target_temp");
|
|
id(disp1).show_component("climateslider");
|
|
id(disp1).show_component("decrease_temp");
|
|
id(disp1).show_component("increase_temp");
|
|
}
|
|
else
|
|
{
|
|
id(disp1).hide_component("target_icon");
|
|
id(disp1).hide_component("target_temp");
|
|
id(disp1).hide_component("climateslider");
|
|
id(disp1).hide_component("decrease_temp");
|
|
id(disp1).hide_component("increase_temp");
|
|
}
|
|
id(disp1).set_component_value("embedded", (embedded_climate) ? 1 : 0);
|
|
|
|
- id: refresh_colors ## Refresh colors of global components
|
|
mode: restart
|
|
then:
|
|
- if:
|
|
condition:
|
|
- binary_sensor.is_on: nextion_init
|
|
#- text_sensor.state: # Is home page visible?
|
|
# id: current_page
|
|
# state: 'home'
|
|
then:
|
|
- lambda: |-
|
|
id(disp1).set_component_font_color("home.date", id(home_date_color));
|
|
id(disp1).set_component_font_color("home.time", id(home_time_color));
|
|
id(disp1).set_component_font_color("home.icon_top_01", id(home_relay1_icon_color));
|
|
id(disp1).set_component_font_color("home.icon_top_02", id(home_relay2_icon_color));
|
|
|
|
- id: refresh_datetime
|
|
mode: restart
|
|
then:
|
|
- if:
|
|
condition:
|
|
- binary_sensor.is_on: nextion_init
|
|
#- text_sensor.state: # Is home page visible?
|
|
# id: current_page
|
|
# state: 'home'
|
|
then:
|
|
- lambda: |-
|
|
std::string time_format_str = id(mui_time_format);
|
|
if (time_format_str.find("%p") != std::string::npos)
|
|
{
|
|
std::string meridiem_text = id(time_provider).now().strftime("%p");
|
|
id(disp1).set_component_text_printf("home.meridiem", "%s", meridiem_text.c_str());
|
|
}
|
|
else { id(disp1).set_component_text_printf("home.meridiem", " "); }
|
|
if (time_format_str.find("%-H") != std::string::npos) { time_format_str = time_format_str.replace(time_format_str.find("%-H"), sizeof("%-H")-1, to_string((int)(id(time_provider).now().hour))); }
|
|
if (time_format_str.find("%-I") != std::string::npos)
|
|
{
|
|
if (id(time_provider).now().hour>12)
|
|
{
|
|
time_format_str = time_format_str.replace(time_format_str.find("%-I"), sizeof("%-I")-1, to_string((int)(id(time_provider).now().hour-12)));
|
|
}
|
|
else if (id(time_provider).now().hour==0)
|
|
{
|
|
time_format_str = time_format_str.replace(time_format_str.find("%-I"), sizeof("%-I")-1, "12");
|
|
}
|
|
else
|
|
{
|
|
time_format_str = time_format_str.replace(time_format_str.find("%-I"), sizeof("%-I")-1, to_string((int)(id(time_provider).now().hour)));
|
|
}
|
|
}
|
|
std::string time_text = id(time_provider).now().strftime(time_format_str);
|
|
id(disp1).set_component_text_printf("home.time", "%s", time_text.c_str());
|
|
|
|
- id: refresh_relays
|
|
mode: restart
|
|
then:
|
|
- if:
|
|
condition:
|
|
- binary_sensor.is_on: nextion_init
|
|
#- text_sensor.state: # Is home page visible?
|
|
# id: current_page
|
|
# state: 'home'
|
|
then:
|
|
- lambda: |-
|
|
// Chips - Relays
|
|
if (id(relay_1).state) id(disp1).set_component_text_printf("home.icon_top_01", "%s", id(home_relay1_icon).c_str());
|
|
else id(disp1).set_component_text_printf("home.icon_top_01", "\uFFFF");
|
|
if (id(relay_2).state) id(disp1).set_component_text_printf("home.icon_top_02", "%s", id(home_relay2_icon).c_str());
|
|
else id(disp1).set_component_text_printf("home.icon_top_02", "\uFFFF");
|
|
// Hardware buttons - Fallback mode
|
|
if (id(relay_1).state or (id(relay1_fallback).state and not id(api_status).state)) id(disp1).send_command_printf("home.left_bt_pic.pic=%i", (id(relay_1).state) ? 78 : 77);
|
|
if (id(relay_2).state or (id(relay2_fallback).state and not id(api_status).state)) id(disp1).send_command_printf("home.right_bt_pic.pic=%i", (id(relay_2).state) ? 78 : 77);
|
|
|
|
- id: refresh_chips_climate
|
|
mode: restart
|
|
then:
|
|
- if:
|
|
condition:
|
|
- binary_sensor.is_on: nextion_init
|
|
- lambda: !lambda 'return (not ${embedded_thermostat_disabled});'
|
|
- lambda: !lambda 'return id(is_embedded_thermostat);'
|
|
then:
|
|
- lambda: |-
|
|
if (${verbose_log}) ESP_LOGD("script.refresh_chips_climate", "thermostat_embedded.action=%i", int(id(thermostat_embedded).action));
|
|
switch (int(id(thermostat_embedded).action)) // CLIMATE_ACTION_OFF = 0, CLIMATE_ACTION_COOLING = 2, CLIMATE_ACTION_HEATING = 3, CLIMATE_ACTION_IDLE = 4, CLIMATE_ACTION_DRYING = 5, CLIMATE_ACTION_FAN = 6
|
|
{
|
|
case 0: //CLIMATE_ACTION_OFF
|
|
if (${verbose_log}) ESP_LOGD("script.refresh_chips_climate", "thermostat_embedded.mode=%i", int(id(thermostat_embedded).mode));
|
|
switch (int(id(thermostat_embedded).mode)) // CLIMATE_MODE_OFF = 0, CLIMATE_MODE_HEAT_COOL = 1, CLIMATE_MODE_COOL = 2, CLIMATE_MODE_HEAT = 3, CLIMATE_MODE_FAN_ONLY = 4, CLIMATE_MODE_DRY = 5, CLIMATE_MODE_AUTO = 6
|
|
{
|
|
case 0: //CLIMATE_MODE_OFF
|
|
id(disp1).set_component_text_printf("home.icon_top_03", "%s", "\uFFFF"); // (E424) Don't show icon when off
|
|
id(disp1).set_component_font_color("home.icon_top_03", 35921);
|
|
break;
|
|
case 1: //CLIMATE_MODE_HEAT_COOL
|
|
id(disp1).set_component_text_printf("home.icon_top_03", "%s", "\uE069");
|
|
id(disp1).set_component_font_color("home.icon_top_03", 35921);
|
|
break;
|
|
case 2: //CLIMATE_MODE_COOL
|
|
id(disp1).set_component_text_printf("home.icon_top_03", "%s", "\uE716");
|
|
id(disp1).set_component_font_color("home.icon_top_03", 35921);
|
|
break;
|
|
case 3: //CLIMATE_MODE_HEAT
|
|
id(disp1).set_component_text_printf("home.icon_top_03", "%s", "\uE237");
|
|
id(disp1).set_component_font_color("home.icon_top_03", 35921);
|
|
break;
|
|
case 4: //CLIMATE_MODE_FAN_ONLY
|
|
id(disp1).set_component_text_printf("home.icon_top_03", "%s", "\uE20F");
|
|
id(disp1).set_component_font_color("home.icon_top_03", 35921);
|
|
break;
|
|
case 5: //CLIMATE_MODE_DRY
|
|
id(disp1).set_component_text_printf("home.icon_top_03", "%s", "\uE58D");
|
|
id(disp1).set_component_font_color("home.icon_top_03", 35921);
|
|
break;
|
|
case 6: //CLIMATE_MODE_AUTO
|
|
id(disp1).set_component_text_printf("home.icon_top_03", "%s", "\uEE8D");
|
|
id(disp1).set_component_font_color("home.icon_top_03", 35921);
|
|
break;
|
|
}
|
|
case 2: //CLIMATE_ACTION_COOLING
|
|
id(disp1).set_component_text_printf("home.icon_top_03", "%s", "\uE716");
|
|
id(disp1).set_component_font_color("home.icon_top_03", 1055);
|
|
break;
|
|
case 3: //CLIMATE_ACTION_HEATING
|
|
id(disp1).set_component_text_printf("home.icon_top_03", "%s", "\uE237");
|
|
id(disp1).set_component_font_color("home.icon_top_03", 64164);
|
|
break;
|
|
case 4: //CLIMATE_ACTION_IDLE
|
|
id(disp1).set_component_text_printf("home.icon_top_03", "%s", "\uE50E"); // mdi:thermometer
|
|
id(disp1).set_component_font_color("home.icon_top_03", 35921);
|
|
break;
|
|
case 5: //CLIMATE_ACTION_DRYING
|
|
id(disp1).set_component_text_printf("home.icon_top_03", "%s", "\uE58D");
|
|
id(disp1).set_component_font_color("home.icon_top_03", 64704);
|
|
break;
|
|
case 6: //CLIMATE_ACTION_FAN
|
|
id(disp1).set_component_text_printf("home.icon_top_03", "%s", "\uE20F");
|
|
id(disp1).set_component_font_color("home.icon_top_03", 1530);
|
|
break;
|
|
}
|
|
|
|
- id: refresh_wifi_icon
|
|
mode: restart
|
|
then:
|
|
- if:
|
|
condition:
|
|
- binary_sensor.is_on: nextion_init
|
|
#- text_sensor.state: # Is home page visible?
|
|
# id: current_page
|
|
# state: 'home'
|
|
then:
|
|
# Update Wi-Fi icon
|
|
- if:
|
|
condition:
|
|
wifi.connected:
|
|
then:
|
|
- if:
|
|
condition:
|
|
api.connected:
|
|
then:
|
|
- lambda: id(disp1).set_component_value("home.api",1);
|
|
- lambda: id(disp1).set_component_text_printf("home.wifi_icon", "%s", "\uE5A8");
|
|
- lambda: id(disp1).set_component_font_color("home.wifi_icon", 33808);
|
|
else:
|
|
- lambda: id(disp1).set_component_value("home.api",0);
|
|
- lambda: id(disp1).set_component_text_printf("home.wifi_icon", "%s", "\uF256");
|
|
- lambda: id(disp1).set_component_font_color("home.wifi_icon", 63488);
|
|
else:
|
|
- lambda: id(disp1).set_component_value("home.api",0);
|
|
- lambda: id(disp1).set_component_text_printf("home.wifi_icon", "%s", "\uE5A9");
|
|
- lambda: id(disp1).set_component_font_color("home.wifi_icon", 63488);
|
|
|
|
- id: update_page_home
|
|
mode: restart
|
|
then:
|
|
- script.execute:
|
|
id: refresh_datetime
|
|
- script.execute:
|
|
id: refresh_relays
|
|
- script.execute:
|
|
id: refresh_wifi_icon
|
|
- script.execute:
|
|
id: refresh_chips_climate
|
|
- if:
|
|
condition:
|
|
- binary_sensor.is_on: nextion_init
|
|
- text_sensor.state: # Is home page visible?
|
|
id: current_page
|
|
state: 'home'
|
|
then:
|
|
- lambda: |-
|
|
// Update home.entity variable
|
|
if (id(is_embedded_thermostat) and not (${embedded_thermostat_disabled})) id(disp1).set_component_text_printf("home.entity", "embedded_climate");
|
|
else id(disp1).set_component_text_printf("home.entity", "");
|
|
|
|
- id: update_page_climate
|
|
mode: restart
|
|
then:
|
|
- if:
|
|
condition:
|
|
- binary_sensor.is_on: nextion_init
|
|
- text_sensor.state: # Is climate page visible?
|
|
id: current_page
|
|
state: 'climate'
|
|
- lambda: !lambda return id(is_embedded_thermostat_visible);
|
|
- lambda: !lambda 'return (not ${embedded_thermostat_disabled});'
|
|
then: # Embedded thermostat is visible
|
|
# Update slider, current temperature & target temperature
|
|
- script.execute:
|
|
id: set_climate
|
|
current_temp: !lambda "return id(thermostat_embedded).current_temperature;"
|
|
target_temp: !lambda "return id(thermostat_embedded).target_temperature;"
|
|
temp_step: !lambda "return int(round(${embedded_thermostat_visual_temperature_step}*10));"
|
|
total_steps: !lambda |-
|
|
float temp_step = ${embedded_thermostat_visual_temperature_step};
|
|
float temp_offset = ${embedded_thermostat_visual_min_temperature};
|
|
float temp_max = ${embedded_thermostat_visual_max_temperature};
|
|
float total_steps = (temp_max-temp_offset)/temp_step;
|
|
return int(round(total_steps));
|
|
slider_val: !lambda |-
|
|
float temp_step = ${embedded_thermostat_visual_temperature_step};
|
|
float temp_offset = ${embedded_thermostat_visual_min_temperature};
|
|
return int(round((10*id(thermostat_embedded).target_temperature-temp_offset)/temp_step));
|
|
temp_offset: !lambda "return int(round(${embedded_thermostat_visual_min_temperature}*10));"
|
|
climate_icon: ""
|
|
embedded_climate: True
|
|
|
|
# Update target temp icon
|
|
- lambda: |-
|
|
if (${verbose_log}) ESP_LOGD("script.update_page_climate", "thermostat_embedded.action=%i", int(id(thermostat_embedded).action));
|
|
switch (int(id(thermostat_embedded).action)) // CLIMATE_ACTION_OFF = 0, CLIMATE_ACTION_COOLING = 2, CLIMATE_ACTION_HEATING = 3, CLIMATE_ACTION_IDLE = 4, CLIMATE_ACTION_DRYING = 5, CLIMATE_ACTION_FAN = 6
|
|
{
|
|
case 0: //CLIMATE_ACTION_OFF
|
|
if (${verbose_log}) ESP_LOGD("script.update_page_climate", "thermostat_embedded.mode=%i", int(id(thermostat_embedded).mode));
|
|
switch (int(id(thermostat_embedded).mode)) // CLIMATE_MODE_OFF = 0, CLIMATE_MODE_HEAT_COOL = 1, CLIMATE_MODE_COOL = 2, CLIMATE_MODE_HEAT = 3, CLIMATE_MODE_FAN_ONLY = 4, CLIMATE_MODE_DRY = 5, CLIMATE_MODE_AUTO = 6
|
|
{
|
|
case 0: //CLIMATE_MODE_OFF
|
|
id(disp1).set_component_text_printf("climate.target_icon", "%s", "\uFFFF"); // (E424) Don't show icon when off
|
|
id(disp1).set_component_font_color("climate.target_icon", 35921);
|
|
break;
|
|
case 1: //CLIMATE_MODE_HEAT_COOL
|
|
id(disp1).set_component_text_printf("climate.target_icon", "%s", "\uE069");
|
|
id(disp1).set_component_font_color("climate.target_icon", 35921);
|
|
break;
|
|
case 2: //CLIMATE_MODE_COOL
|
|
id(disp1).set_component_text_printf("climate.target_icon", "%s", "\uE716");
|
|
id(disp1).set_component_font_color("climate.target_icon", 1055);
|
|
break;
|
|
case 3: //CLIMATE_MODE_HEAT
|
|
id(disp1).set_component_text_printf("climate.target_icon", "%s", "\uE237");
|
|
id(disp1).set_component_font_color("climate.target_icon", 64164);
|
|
break;
|
|
case 4: //CLIMATE_MODE_FAN_ONLY
|
|
id(disp1).set_component_text_printf("climate.target_icon", "%s", "\uE20F");
|
|
id(disp1).set_component_font_color("climate.target_icon", 35921);
|
|
break;
|
|
case 5: //CLIMATE_MODE_DRY
|
|
id(disp1).set_component_text_printf("climate.target_icon", "%s", "\uE58D");
|
|
id(disp1).set_component_font_color("climate.target_icon", 64704);
|
|
break;
|
|
case 6: //CLIMATE_MODE_AUTO
|
|
id(disp1).set_component_text_printf("climate.target_icon", "%s", "\uEE8D");
|
|
id(disp1).set_component_font_color("climate.target_icon", 35921);
|
|
break;
|
|
}
|
|
case 2: //CLIMATE_ACTION_COOLING
|
|
id(disp1).set_component_text_printf("climate.target_icon", "%s", "\uE716");
|
|
id(disp1).set_component_font_color("climate.target_icon", 1055);
|
|
break;
|
|
case 3: //CLIMATE_ACTION_HEATING
|
|
id(disp1).set_component_text_printf("climate.target_icon", "%s", "\uE237");
|
|
id(disp1).set_component_font_color("climate.target_icon", 64164);
|
|
break;
|
|
case 4: //CLIMATE_ACTION_IDLE
|
|
id(disp1).set_component_text_printf("climate.target_icon", "%s", "\uE50E"); // mdi:thermometer
|
|
id(disp1).set_component_font_color("climate.target_icon", 35921);
|
|
break;
|
|
case 5: //CLIMATE_ACTION_DRYING
|
|
id(disp1).set_component_text_printf("climate.target_icon", "%s", "\uE58D");
|
|
id(disp1).set_component_font_color("climate.target_icon", 64704);
|
|
break;
|
|
case 6: //CLIMATE_ACTION_FAN
|
|
id(disp1).set_component_text_printf("climate.target_icon", "%s", "\uE20F");
|
|
id(disp1).set_component_font_color("climate.target_icon", 1530);
|
|
break;
|
|
}
|
|
|
|
# Update buttons bar
|
|
- lambda: |-
|
|
if (${verbose_log}) ESP_LOGD("script.update_page_climate", "Updating buttons bar");
|
|
// Hide not supported hotspots
|
|
id(disp1).hide_component("climate.button01");
|
|
id(disp1).hide_component("climate.button02");
|
|
id(disp1).show_component("climate.button03"); //Heat
|
|
id(disp1).hide_component("climate.button04");
|
|
id(disp1).hide_component("climate.button05");
|
|
id(disp1).hide_component("climate.button06");
|
|
id(disp1).show_component("climate.button07"); //Off
|
|
// Set buttons colors
|
|
id(disp1).set_component_font_color("climate.button01_icon", 10597);
|
|
id(disp1).set_component_font_color("climate.button02_icon", 10597);
|
|
id(disp1).set_component_font_color("climate.button03_icon", (id(thermostat_embedded).mode==climate::CLIMATE_MODE_HEAT) ? 64164 : 48631);
|
|
id(disp1).set_component_font_color("climate.button04_icon", 10597);
|
|
id(disp1).set_component_font_color("climate.button05_icon", 10597);
|
|
id(disp1).set_component_font_color("climate.button06_icon", 10597);
|
|
id(disp1).set_component_font_color("climate.button07_icon", (id(thermostat_embedded).mode==climate::CLIMATE_MODE_OFF) ? 35921 : 48631);
|
|
|
|
climate:
|
|
- platform: thermostat
|
|
name: ${device_name} Thermostat
|
|
id: thermostat_embedded
|
|
sensor: temp_nspanel
|
|
min_heating_off_time: ${embedded_thermostat_min_heating_off_time}s
|
|
min_heating_run_time: ${embedded_thermostat_min_heating_run_time}s
|
|
min_idle_time: ${embedded_thermostat_min_idle_time}s
|
|
visual:
|
|
min_temperature: ${embedded_thermostat_visual_min_temperature} ${embedded_thermostat_temp_units}
|
|
max_temperature: ${embedded_thermostat_visual_max_temperature} ${embedded_thermostat_temp_units}
|
|
temperature_step: ${embedded_thermostat_visual_temperature_step} ${embedded_thermostat_temp_units}
|
|
# target_temperature: 0.5 #!lambda "return ${embedded_thermostat_visual_target_temperature_step};"
|
|
# current_temperature: 0.1 #!lambda "return ${embedded_thermostat_visual_current_temperature_step};"
|
|
heat_action:
|
|
- switch.turn_on: relay_${embedded_thermostat_heater_relay}
|
|
idle_action:
|
|
- switch.turn_off: relay_${embedded_thermostat_heater_relay}
|
|
default_preset: "Off"
|
|
on_boot_restore_from: memory
|
|
preset:
|
|
- name: "Off"
|
|
default_target_temperature_low: ${embedded_thermostat_visual_min_temperature} ${embedded_thermostat_temp_units}
|
|
mode: "off"
|
|
- name: Home
|
|
default_target_temperature_low: 21 ${embedded_thermostat_temp_units}
|
|
internal: ${embedded_thermostat_disabled}
|
|
on_state:
|
|
- logger.log: Climate state changed - Start
|
|
- script.execute:
|
|
id: update_page_climate
|
|
- logger.log: Climate state changed - End
|