Add-on support (#977)

Moved climate settings to an add-on.
Updated documentation.
This commit is contained in:
Edward Firmo
2023-08-09 19:00:09 +02:00
committed by GitHub
parent 8650b0814c
commit f1456dba3a
4 changed files with 515 additions and 329 deletions

View File

@@ -7,7 +7,9 @@
 
## General
- Thanks for 600 stars.
First of all, we wanna say a big thanks to all of you who starred this project in GitHub. It's amazing how the number of stars keeps growing and is now around 650!!!
After a long wait since the latest big release, which we used to leave our desks for a while and enjoy some vacations time with our beloved ones, we are proud to introduce the version 4.0, which adds new screens and makes your panel more flexible and robust.
###
@@ -34,23 +36,33 @@ Since in this update lots of input to the blueprint changed, we highly recommend
 
## Overview of all changes
1. Alarm control panel
2. Support to sensor display precision from Home Assistant (#880)
3. Filtered device list (#881)
4. New language selector (#882)
5. Removed `settings_entity` (#887)
6. Support for US model on landscape mode (#890)
7. API status indication on the panel ([#5ff5d35](https://github.com/Blackymas/NSPanel_HA_Blueprint/commit/5ff5d35833be1a1cf9ca0f570662456058980024))
8. Light & cover settings pages will show only the supported features (#896)
9. New "Fan speed page" (#897)
10. Select wake-up page (#898)
11. Panel's local control
12. Embedded thermostat/heater (#917)
1. Add-ons support
- Embedded thermostat/heater (#917)
2. Alarm control panel
3. Support to sensor display precision from Home Assistant (#880)
4. Filtered device list (#881)
5. New language selector (#882)
6. Removed `settings_entity` (#887)
7. Support for US model on landscape mode (#890)
8. API status indication on the panel ([#5ff5d35](https://github.com/Blackymas/NSPanel_HA_Blueprint/commit/5ff5d35833be1a1cf9ca0f570662456058980024))
9. Light & cover settings pages will show only the supported features (#896)
10. New "Fan speed page" (#897)
11. Select wake-up page (#898)
12. Panel's local control
 
## Details of all changes
### 1. Alarm control panel
### 1. Support to add-ons
We are trying to make your panel more usefull and more robust by changing some of the functionalitites to run internally in the panel, even when the Wi-Fi network or Home Assistant are not available, however, every new functionality takes a bit from the ESP embedded in your panel and increases the complexity, and having a code cabaple to adapt to all the different user cases will be very complex and certainly will exceed the available memory.
The first add-on available is an **[embedded thermostat/heater](/docs/us/addon_climate.md)**, able to control it locally even when your Wi-Fi is out or Home Assistant is unavailable.
Please refeer to the [documentation](/docs/us/addon_climate.md) to get more details on how to enable this add-on.
 
### 2. Alarm control panel
recommend api-encryption: https://esphome.io/components/api.html#configuration-variables
and, of course warn the user that this is a possible security issue.
Anyways - this is also done with many other projects, AND it would require that a possible hacker is already inside the internal (or mayby iot) (W)LAN - and at this point....
@@ -60,37 +72,37 @@ recommend to have a big warning, and that the user has to take care about e.g.:
- enable api-encryption
 
### 2. Support to sensor display precision from Home Assistant
### 3. Support to sensor display precision from Home Assistant
Now the values shown in your panel will follow the [sensor display precision](https://www.home-assistant.io/blog/2023/03/01/release-20233/#sensor-display-precision) provided by Home Assistant.
=> If you have problems with a value exceeding the available space in your panel, please reduce the number of decimals using Home Assistant [sensor display precision](https://www.home-assistant.io/blog/2023/03/01/release-20233/#sensor-display-precision).
 
### 3. Filtered device list
### 4. Filtered device list
When selecting the NSPanel on the automation, only ESP32 devices will be shown, making easier to find your panel.
![image](https://github.com/Blackymas/NSPanel_HA_Blueprint/assets/94725493/0e66bd6b-23c3-4014-8603-958acea48462)
 
### 4. New language selector
### 5. New language selector
Starts using the new language selector release with HA 2023.5.0 and based on RFC 5646, which will increase reliability and standardization of the code.
Althougt this is not visible for users at the first view, it will enable the use of more granualar language selections (like pt-BR vs pt-PT or en-US vs en-UK) if needed in the future.
=> If you are an existing users, please remember to select your language again after the update, as the previous selection will be invalid.
 
### 5. Removed `settings_entity`
### 6. Removed `settings_entity`
The entity `sensor.xxxxx_settings_entity` was previously used by ESPHome to to transfer information about the selected entity on the settings page to the Blueprint, enabling the transfer of settings from different instances of the blueprint with the use of service `esphome.xxxxx_set_settings_entity`. This mechanism was a bit fragile and not user friendly.
With this version the information about the entity shown will be part of the `sensor.xxxxx_nspanel_event` and the settings pages will be called with the service `esphome.xxxxx_open_entity_settings_page`.
Apart of a cleaner device page, this change should be transparent for most users. If you have made automations based on the removed elements, please update it using the new service.
 
### 6. Support for US model on landscape mode
### 7. Support for US model on landscape mode
If you are using a panel model US in landscape mode, you can now use `nspanel_us_land.tft` where the bars related to the hardware buttons will be located at the right, closer to the respective buttons and fixing the offset on the touch screen when using `nspanel_eu.tft` into a US panel.
=> The hardware buttons labels are hidden in this format, as Nextion cannot support rotated text.
 
### 7. API status indication on the panel
### 8. API status indication on the panel
Now the Wi-Fi icon in the panel (at the right side of the time) will show one of 3 possible states:
![wifi_gray](https://github.com/Blackymas/NSPanel_HA_Blueprint/assets/94725493/8e462abb-68d7-4ade-b3c1-ef115aa66c2c) The panel is connected to the Wi-Fi and the API is connected to Home Assistant (mdi:wifi)
@@ -100,16 +112,16 @@ Now the Wi-Fi icon in the panel (at the right side of the time) will show one of
![wifi-off_red](https://github.com/Blackymas/NSPanel_HA_Blueprint/assets/94725493/c8b15d1f-0950-42d7-84d1-fca8610543c0) The panel is **NOT** connected to the Wi-Fi (mdi:wifi-off)
 
### 8. Light & cover settings pages will show only the supported features
### 9. Light & cover settings pages will show only the supported features
Now when long press a button conneted to a light or a cover, the detailed light settings page will open only when the entity supports advanced control, and the detailed page will only show the controls supported by that entity.
 
### 9. New "Fan speed page"
### 10. New "Fan speed page"
If you have a connected fan supporting speed control, now you are able to control it's speed from your panel. Just add the new fan to one of the buttons pages or to the hardware buttons and a long press on those buttons will pop up the new "Fan speed page":
![image](https://github.com/Blackymas/NSPanel_HA_Blueprint/assets/94725493/4167928e-6822-4db6-a24b-f8a1d52806f5)
 
### 10. Select wake-up page
### 11. Select wake-up page
Now you can select the wake-up page on the blueprint settings:
![image](https://github.com/Blackymas/NSPanel_HA_Blueprint/assets/94725493/30b54682-f1ca-43ac-80af-f89459a6ffd7)
@@ -117,32 +129,13 @@ Now you can select the wake-up page on the blueprint settings:
This selected page will be shown after a boot (after the boot page) and with a touch in the screen when on screen saver page. After showing this wake-up page, all the previous behavior for closing the page (with a click or after a timeout) will be the same.
 
### 11. Panel's local control
### 12. Panel's local control
We are trying to make your panel as autonomous as possible by moving some of the controls from the Blueprint to ESPHome. This will reduce the load in your network and Home Assistant, but also will make a more reliable system capable to do it's core functionality even when the network is unavailable or Home Assistant is restarting.
With this version, the following engines have been moved to your panel (local control):
- Time display
- Physical relay control (when hardware left button is connected to relay 1 or right button to relay 2)
- Embedded thermostat (see bellow)
 
### 12. Embedded thermostat/heater
Now you can use your panel to control a heater and let your ambient more confortable, and you will be able to control it locally even when your Wi-Fi is out or Home Assistant is unavailable.
in order to have this enable, add the following to your substitutions:
```yaml
substitutions:
embedded_thermostat_disabled: "false"
embedded_thermostat_heater_relay: "1" # Select 1 for "Relay 1" or 2 for "Relay 2"
```
For more advanced settings, please take a look at the full documentation.
===>>> Add documentation before release <<<===
- How to enable
- Substitutions
- etc
&nbsp;
## Next topics we are currently working on
See here: https://github.com/Blackymas/NSPanel_HA_Blueprint/labels/roadmap

95
docs/en/addon_climate.md Normal file
View File

@@ -0,0 +1,95 @@
# Add-on: Climate
## Description
This add-on enables the use of your panel's relays to act as a thermostat (heater only for now) using the internal temperature sensor and independent of the network availability.
### Attention
The NSPanel is limited to 2A per relay. Don't use it for directly power your heater if exceeding the panel specifications:
- 150W/110V/Gang, 300W/110V/Total
- 300W/220V/Gang, 600W/220V/Total
More details on the [Sonoff NSPanel's page](https://sonoff.tech/product/central-control-panel/nspanel/) and the [product specifications document](https://sonoff.tech/wp-content/uploads/2021/11/%E4%BA%A7%E5%93%81%E5%8F%82%E6%95%B0%E8%A1%A8-NSPanel-20210831.pdf).
&nbsp;
## Installation
You will need to add the reference to the `addon_climate` file on your ESPHome settings in the `package` section and after te `remote_package` (base code), as shown bellow:
```yaml
substitutions:
###### CHANGE ME START ######
device_name: "YOUR_NSPANEL_NAME"
wifi_ssid: !secret wifi_ssid
wifi_password: !secret wifi_password
nextion_update_url: "http://homeassistant.local:8123/local/nspanel_eu.tft"
addon_climate_heater_relay: "1"
##### CHANGE ME END #####
packages:
remote_package:
url: https://github.com/Blackymas/NSPanel_HA_Blueprint
ref: main
files: [nspanel_esphome.yaml]
refresh: 300s
addon_climate:
url: https://github.com/Blackymas/NSPanel_HA_Blueprint
ref: main
files: [nspanel_esphome_addon_climate.yaml]
refresh: 300s
```
&nbsp;
## Configuration
The following keys are available to be used in your `substitutions`:
|Key|Required|Supported values|Default|Description|
|:-|:-:|:-:|:-:|:-|
|addon_climate_heater_relay|Mandatory|`1` or `2`|`0` (disabled)|Relay used for conrol the heater. User `1` for "Relay 1" or `2` for "Relay 2".|
|addon_climate_temp_units|Optional|`°C` or `°F`|`°C`|Temperature unit.|
|addon_climate_min_heating_off_time|Optional|Positive integer representing the number of seconds|`300`|Minimum duration (in seconds) the heating action must be disengaged before it may be engaged.|
|addon_climate_min_heating_run_time|Optional|Positive integer representing the number of seconds|`300`|Minimum duration (in seconds) the heating action must be engaged before it may be disengaged.|
|addon_climate_min_idle_time|Optional|Positive integer representing the number of seconds|`30`|Minimum duration (in seconds) the idle action must be active before calling another climate action.|
|addon_climate_visual_min_temperature|Optional|Number representing a temperature in the selected unit|`5`|The minimum temperature the climate device can reach. Used to set the range of the frontend gauge.|
|addon_climate_visual_max_temperature|Optional|Number representing a temperature in the selected unit|`25`|The maximum temperature the climate device can reach. Used to set the range of the frontend gauge.|
|addon_climate_visual_temperature_step|Optional|Number representing a temperature in the selected unit|`0.5`|The granularity with which the target temperature can be controlled.|
- All values must be delimited with `""`
- For more details on the keys, please take a look at [ESPHome Base Climate Configurations](https://esphome.io/components/climate/index.html#base-climate-configuration) and [ESPHome Climate Thermostat - Additional actions behavior](https://esphome.io/components/climate/thermostat.html#additional-actions-behavior).
### Example:
```yaml
substitutions:
###### CHANGE ME START ######
device_name: "YOUR_NSPANEL_NAME"
wifi_ssid: !secret wifi_ssid
wifi_password: !secret wifi_password
nextion_update_url: "http://homeassistant.local:8123/local/nspanel_us.tft"
addon_climate_heater_relay: "1" #Use relay 1
addon_climate_temp_units: "°F" #Temperatures in Fahrenheit
addon_climate_visual_min_temperature: "40" #Min supported temperature is 40F
addon_climate_visual_max_temperature: "80" #Max supported temperature is 80F
addon_climate_visual_temperature_step: "1" #Temperature granularity is 1F
##### CHANGE ME END #####
packages:
remote_package:
url: https://github.com/Blackymas/NSPanel_HA_Blueprint
ref: main
files: [nspanel_esphome.yaml]
refresh: 300s
addon_climate:
url: https://github.com/Blackymas/NSPanel_HA_Blueprint
ref: main
files: [nspanel_esphome_addon_climate.yaml]
refresh: 300s
```

View File

@@ -28,20 +28,6 @@ substitutions:
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: "0" # Select 1 for "Relay 1", 2 for "Relay 2" or "0" to a dummy switch/disabled
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
@@ -62,7 +48,8 @@ substitutions:
##### WIFI SETUP #####
wifi:
networks:
- ssid: ${wifi_ssid}
- id: wifi_default
ssid: ${wifi_ssid}
password: ${wifi_password}
hidden: ${wifi_hidden}
power_save_mode: none
@@ -523,7 +510,7 @@ api:
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);
ESP_LOGD("global_settings", "embedded_climate: %i", (embedded_climate) ? 1 : 0);
ESP_LOGD("global_settings", "wakeup_page: %i", wakeup_page);
ESP_LOGD("global_settings", "alarm_state: %s", alarm_state.c_str());
}
@@ -546,7 +533,10 @@ api:
id(home_time_color) = time_color;
## Embedded thermostat
- lambda: id(is_embedded_thermostat) = (embedded_climate and not (${embedded_thermostat_disabled}));
- script.execute:
id: addon_climate_global_settings
embedded_climate: !lambda return embedded_climate;
#- lambda: id(is_embedded_thermostat) = embedded_climate;
## Alarm button
- lambda: id(home_alarm) = (alarm_state != "" and not alarm_state.empty());
@@ -632,17 +622,6 @@ globals:
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
@@ -861,8 +840,8 @@ text_sensor:
disabled_by_default: true
##### ESPhome version used to compile the app #####
- platform: version
name: ${device_name} ESPhome Version
- name: ${device_name} ESPhome Version
platform: version
disabled_by_default: true
- platform: wifi_info
@@ -928,7 +907,7 @@ text_sensor:
id(disp1).set_component_text_printf("climate.button05_icon", "%s", "\uE58D"); //mdi:water-percent
id(disp1).set_component_text_printf("climate.button06_icon", "%s", "\uE20F"); //mdi:fan
id(disp1).set_component_text_printf("climate.button07_icon", "%s", "\uE424"); //mdi:power
if (id(is_embedded_thermostat_visible)) id(update_page_climate);
id(addon_climate_update_page_climate);
}
else if (page=="fan")
{
@@ -972,40 +951,10 @@ text_sensor:
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();
}
if (domain == "climate") id(service_call_climate)->execute(entity.c_str(), key.c_str(), value.c_str(), (embedded==1));
else if (domain == "alarm_control_panel") id(service_call_alarm_control_panel)->execute(entity.c_str(), value.c_str());
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();
@@ -1138,13 +1087,6 @@ switch:
then:
- script.execute:
id: refresh_relays
##### PHYSICAL SWITCH 0 (Dummy) - Usend when embedded climate is disabled #####
- name: ${device_name} Relay 0 (dummy)
platform: template
id: relay_0
lambda: !lambda return false;
internal: true
optimistic: true
##### DISPLAY ALWAYS ON #####
- name: ${device_name} Screen Power
@@ -1380,7 +1322,7 @@ script:
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(addon_climate_set_climate).execute(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);
@@ -1483,74 +1425,6 @@ script:
if (id(relay_1).state and (id(relay1_local).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 and (id(relay2_local).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", 64164);
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:
@@ -1591,8 +1465,6 @@ script:
id: refresh_relays
- script.execute:
id: refresh_wifi_icon
- script.execute:
id: refresh_chips_climate
- if:
condition:
- binary_sensor.is_on: nextion_init
@@ -1600,10 +1472,9 @@ script:
id: current_page
state: 'home'
then:
- script.execute:
id: addon_climate_update_page_home
- 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", "");
// Show alarm button
if (id(home_alarm))
{
@@ -1617,147 +1488,88 @@ script:
id(disp1).hide_component("button07_icon");
}
- id: update_page_climate
- id: service_call_alarm_control_panel
mode: restart
parameters:
entity: string
pin: string
then:
- lambda: |-
if (${verbose_log}) ESP_LOGD("service_call_alarm_control_panel", "ESPHome remote service call");
HomeassistantServiceResponse resp;
HomeassistantServiceMap resp_kv;
resp.service = "alarm_control_panel.XXXX"; // DEBUG
resp_kv.key = "entity_id";
resp_kv.value = entity.c_str();
resp.data.push_back(resp_kv);
resp_kv.key = "pin"; // DEBUG
resp_kv.value = pin.c_str();
resp.data.push_back(resp_kv);
id(api_server).send_homeassistant_service_call(resp);
- id: service_call_climate
mode: restart
parameters:
entity: string
key: string
value: string
embedded: bool
then:
- lambda: |-
if (embedded)
id(addon_climate_service_call)->execute(key.c_str(), value.c_str());
else if (key == "set_temperature")
id(ha_call_service)->execute("climate.set_temperature", "temperature", to_string(stof(value) / 10), entity.c_str());
else if (key == "hvac_mode")
id(ha_call_service)->execute("climate.set_hvac_mode", key.c_str(), value.c_str(), entity.c_str());
- id: ha_call_service
mode: restart
parameters:
service: string
key: string
value: string
entity: string
then:
- lambda: |-
if (service != "" and not service.empty())
{
auto ha_event = new esphome::api::CustomAPIDevice();
ha_event->fire_homeassistant_event("esphome.nspanel_service_call",
{
{"service", service},
{"entity", entity},
{"key", key},
{"value", value}
});
}
##### ADD-ONS ############################################################
##### Add-on - Climate #####
- id: addon_climate_service_call
mode: restart
parameters:
key: string
value: string
then:
# Reserved for Add-on Climate
- id: addon_climate_update_page_home
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
# Reserved for Add-on Climate
- id: addon_climate_set_climate
mode: restart
parameters:
embedded_climate: bool
then:
# Reserved for Add-on Climate
- id: addon_climate_global_settings
mode: restart
parameters:
embedded_climate: bool
then:
# Reserved for Add-on Climate
- id: addon_climate_update_page_climate
mode: restart
then:
# Reserved for Add-on Climate

View File

@@ -0,0 +1,286 @@
####################################################################################################
##### NSPanel ESPHome Add-on for Climate control #####
##### Add-on for https://github.com/Blackymas/NSPanel_HA_Blueprint #####
####################################################################################################
substitutions:
### Local thermostat defaults ###
# https://esphome.io/components/climate/thermostat.html
addon_climate_temp_units: "°C"
addon_climate_heater_relay: "0" # Select 1 for "Relay 1", 2 for "Relay 2" or "0" to a dummy switch/disabled
addon_climate_min_heating_off_time: "300"
addon_climate_min_heating_run_time: "300"
addon_climate_min_idle_time: "30"
# https://esphome.io/components/climate/index.html#base-climate-configuration
addon_climate_visual_min_temperature: "5"
addon_climate_visual_max_temperature: "25"
addon_climate_visual_temperature_step: "0.5"
climate:
- platform: thermostat
name: ${device_name} Thermostat
id: thermostat_embedded
sensor: temp_nspanel
min_heating_off_time: ${addon_climate_min_heating_off_time}s
min_heating_run_time: ${addon_climate_min_heating_run_time}s
min_idle_time: ${addon_climate_min_idle_time}s
visual:
min_temperature: ${addon_climate_visual_min_temperature} ${addon_climate_temp_units}
max_temperature: ${addon_climate_visual_max_temperature} ${addon_climate_temp_units}
temperature_step: ${addon_climate_visual_temperature_step} ${addon_climate_temp_units}
# target_temperature: 0.5 #!lambda "return ${addon_climate_visual_target_temperature_step};"
# current_temperature: 0.1 #!lambda "return ${addon_climate_visual_current_temperature_step};"
heat_action:
- switch.turn_on: relay_${addon_climate_heater_relay}
idle_action:
- switch.turn_off: relay_${addon_climate_heater_relay}
default_preset: "Off"
on_boot_restore_from: memory
preset:
- name: "Off"
default_target_temperature_low: ${addon_climate_visual_min_temperature} ${addon_climate_temp_units}
mode: "off"
- name: Home
default_target_temperature_low: 21 ${addon_climate_temp_units}
internal: false
on_state:
- logger.log: Climate state changed - Start
- script.execute:
id: addon_climate_update_page_climate
- logger.log: Climate state changed - End
globals:
##### 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_addon_climate_visible
type: bool
restore_value: false
initial_value: 'false'
script:
- id: !extend addon_climate_update_page_home
mode: restart
then:
- if:
condition:
- binary_sensor.is_on: nextion_init
then:
- lambda: |-
// Update home.entity variable
id(disp1).set_component_text_printf("home.entity", (id(is_embedded_thermostat)) ? "embedded_climate" : "");
//if (id(is_embedded_thermostat)) id(disp1).set_component_text_printf("home.entity", "embedded_climate");
//else id(disp1).set_component_text_printf("home.entity", "");
- if:
condition:
- lambda: !lambda 'return id(is_embedded_thermostat);'
then:
- lambda: |-
// Update chips
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", 64164);
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: !extend addon_climate_service_call
#mode: restart
#parameters:
# key: string
# value: string
then:
- lambda: |-
id(is_addon_climate_visible) = true;
auto call = id(thermostat_embedded).make_call();
if (key == "set_temperature")
{
call.set_target_temperature(stof(value) / 10);
}
else if (key == "hvac_mode")
{
call.set_mode(value);
}
call.perform();
- id: !extend addon_climate_set_climate
then:
- lambda: id(is_addon_climate_visible) = embedded_climate;
- id: !extend addon_climate_global_settings
then:
- lambda: id(is_embedded_thermostat) = embedded_climate;
- id: !extend addon_climate_update_page_climate
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_addon_climate_visible);
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(${addon_climate_visual_temperature_step}*10));"
total_steps: !lambda |-
float temp_step = ${addon_climate_visual_temperature_step};
float temp_offset = ${addon_climate_visual_min_temperature};
float temp_max = ${addon_climate_visual_max_temperature};
float total_steps = (temp_max-temp_offset)/temp_step;
return int(round(total_steps));
slider_val: !lambda |-
float temp_step = ${addon_climate_visual_temperature_step};
float temp_offset = ${addon_climate_visual_min_temperature};
return int(round((10*id(thermostat_embedded).target_temperature-temp_offset)/temp_step));
temp_offset: !lambda "return int(round(${addon_climate_visual_min_temperature}*10));"
climate_icon: ""
embedded_climate: True
# Update target temp icon
- lambda: |-
if (${verbose_log}) ESP_LOGD("script.addon_climate_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.addon_climate_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.addon_climate_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);
switch:
##### PHYSICAL SWITCH 0 (Dummy) - Usend when embedded climate is disabled #####
- name: ${device_name} Relay 0 (dummy)
platform: template
id: relay_0
lambda: !lambda return false;
internal: true
optimistic: true