configs/sthome-ut10.yaml

1489 lines
46 KiB
YAML

packages:
- !include common/wifi.yaml
- !include common/canbus.yaml
substitutions:
name: sthome-ut10
friendly_name: "sthome-ut10"
#ALLOWED_CHARACTERS_FULL: " !#%\"'()+,-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWYZ[]_abcdefghijklmnopqrstuvwxyz{|}°²³µ¿ÁÂÄÅÉÖÚßàáâãäåæçèéêëìíîðñòóôõöøùúûüýþāăąćčďĐđēėęěğĮįıļľŁłńňőřśšťũūůűųźŻżŽžơưșțΆΈΌΐΑΒΓΔΕΖΗΘΚΜΝΠΡΣΤΥΦάέήίαβγδεζηθικλμνξοπρςστυφχψωϊόύώАБВГДЕЖЗИКЛМНОПРСТУХЦЧШЪЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяёђєіїјљњћ"
ALLOWED_CHARACTERS: " !#%\"'()+,-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWYZ[]_abcdefghijklmnopqrstuvwxyz{|}°²³µ•"
DD_MAX_YEARS: "5"
globals:
- id: g_month_idx
type: int
restore_value: yes
initial_value: '0'
- id: g_year_idx
type: int
restore_value: yes
initial_value: '0'
- id: g_options_year
type: char[1 + ${DD_MAX_YEARS} * 5]
restore_value: yes
# initial_value: "{2022\n2023\n2024\n2025\n2026}"
# CAN BUS
- id: can_msgctr
type: int
restore_value: no
- id: g_cb_cache
type: solar::cbf_cache
restore_value: no
esphome:
name: "${name}"
friendly_name: "${friendly_name}"
includes:
- source # copies folder with files to relevant to be included in esphome compile
- <source/solar/cb_frame.h> # angle brackets ensure file is included above globals in main.cpp. Make sure to use include GUARDS in the file to prevent double inclusion
- <source/solar/cbf_store.h>
- <source/solar/cbf_pylon.h>
- <source/solar/cbf_store_pylon.h>
- <source/solar/cbf_sthome.h>
- <source/solar/cbf_store_sthome.h>
- <source/solar/cbf_cache.h>
on_boot:
- priority: 600 # This is where most sensors are set up (higher number means higher priority)
then:
- ds1307.read_time: # read the RTC time
- lambda: |-
id(can_msgctr) = 0;
id(update_heating_display).execute(false);
esp32:
board: esp32-s3-devkitc-1
flash_size: 16MB
framework:
type: esp-idf
# sdkconfig_options:
# CONFIG_ESP32_S3_BOX_BOARD: "y"
psram:
mode: octal
speed: 80MHz
# Enable logging
logger:
level: very_verbose
initial_level: very_verbose
logs:
canbus: INFO
touchscreen: INFO
xpt2046: INFO
script: INFO
lvgl: INFO
ili9xxx: INFO
# Enable Home Assistant API
api:
encryption:
key: "NApAUAUGkzfbNhOikFb2k+AIE8LMr7O8Ck15IaszrUU="
ota:
- platform: esphome
password: "d669ae14ff964bb0f195e820118af8e3"
wifi:
power_save_mode: none
manual_ip:
static_ip: 10.0.2.10
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "${name} Fallback Hotspot"
password: "YmuU9F5YgBZ1"
captive_portal:
time:
- platform: ds1307
# repeated synchronization is not necessary unless the external RTC
# is much more accurate than the internal clock
update_interval: never
- platform: homeassistant
id: time_source
#update_interval: 360min # Change sync interval from default 5min to 6 hours
on_time_sync:
then:
- ds1307.write_time:
# - if: # Publish the time the device was last restarted, but only once.
# condition:
# lambda: 'return id(device_last_restart).state == "";'
# then:
# - text_sensor.template.publish:
# id: device_last_restart
# state: !lambda 'return id(time_source).now().strftime("%a %d %b %Y - %I:%M:%S %p");'
# - script.execute: time_update
# - script.execute: init_calendar
# on_time:
# - minutes: '*'
# seconds: '*'
# then:
# - script.execute: time_update
# - lambda: |-
# id(get_calendar_days_state).execute("T");
#- script.execute: get_calendar_days_state
#
# - hours: 1,2,3,4
# minutes: 5
# seconds: 0
# then:
# - switch.turn_on: switch_antiburn
# - hours: 1,2,3,4
# minutes: 35
# seconds: 0
# then:
# - switch.turn_off: switch_antiburn
font:
- file: "gfonts://Roboto"
id: roboto_200
size: 200
bpp: 4
glyphs: [
0123456789,.,°,a,n,
"\u0020", # space
"\u002D", # minus
"\u003A", # colon
"\u003F", # question mark
]
- file: "gfonts://Roboto"
id: roboto_192
size: 192
bpp: 4
glyphs: [
0123456789,.,°,a,n,
"\u0020", # space
"\u002D", # minus
"\u003A", # colon
"\u003F", # question mark
]
- file: "gfonts://Roboto"
id: geyser_temperature_font2
size: 60
bpp: 4
glyphs: [
°,C,
]
- file: "gfonts://Roboto"
id: geyser_temperature_font3
size: 30
bpp: 4
glyphs: [
b,o,m,p,t,
]
- file: "fonts/misc/materialdesignicons-webfont.ttf"
id: font_icon_small
size: 24 #45
glyphs: [
"\U0000F5A9",
]
color:
- id: grey_light
hex: 'e0e0e0'
image:
# - file: https://esphome.io/_static/favicon-512x512.png
# id: boot_logo
# resize: 200x200
# type: RGB565
# transparency: alpha_channel
- file: mdi:fire
id: icon_fire
resize: 100x100
type: BINARY
- file: mdi:transmission-tower
id: icon_utility
resize: 80x80
type: BINARY
sun:
id: sun_sensor
latitude: !secret latitude
longitude: !secret longitude
interval:
- interval: 30s
then:
- script.execute: send_info_request
- interval: 500ms
then:
- script.execute: send_quick_update_request
spi:
- id: spi_bus0
clk_pin: GPIO12
mosi_pin: GPIO11
miso_pin: GPIO13
interface: any
- id: spi_bus1
clk_pin: GPIO42
mosi_pin: GPIO21
miso_pin: GPIO20
interface: any
i2c:
- id: i2c_bus0
sda: GPIO8
scl: GPIO9
scan: true
#one_wire:
# - platform: gpio
# pin: GPIO4
# id: temperature_sensors
# CAN BUS
canbus:
- platform: mcp2515
cs_pin: GPIO10
spi_id: spi_bus0
id: canbus_sthome
can_id: ${CB_CANBUS_ID10}
bit_rate: 500KBPS
on_frame:
- can_id: 0
can_id_mask: 0
then:
- lambda: |-
id(can_msgctr)++;
using namespace solar;
auto time_obj = id(time_source).now();
//ESP_LOGI("CB REC", "%s", id(time_source).now().strftime("%a %d %b %Y - %I:%M:%S %p"));
if(time_obj.is_valid()) {
if(can_id >= 0x350 && can_id < 0x380) {
auto cbitem = cbf_store_pylon(id(can_msgctr), can_id, x, remote_transmission_request, time_obj.timestamp);
bool publish = id(g_cb_cache).additem(cbitem);
if(publish) {
//ESP_LOGI(cbitem.tag().c_str(), "%s", cbitem.to_string().c_str());
switch (can_id) {
case cbf_pylon::CB_BATTERY_STATE:
{
uint soc = static_cast<uint16_t>((x[1] << 8) + x[0]);
uint soh = static_cast<uint16_t>((x[3] << 8) + x[2]);
ESP_LOGI(cbitem.tag().c_str(), "SOC= %d%% SOH= %d%%", soc, soh);
}
}
}
}
else if(can_id >= 0x400 && can_id <= 0x580) {
auto cbitem = cbf_store_sthome(id(can_msgctr), can_id, x, remote_transmission_request, time_obj.timestamp);
bool publish = id(g_cb_cache).additem(cbitem);
//ESP_LOGI(cbitem.tag().c_str(), "%s P[%s]", cbitem.to_string().c_str(), publish ? "Y" : "N");
if(publish) {
ESP_LOGI(cbitem.tag().c_str(), "%s", cbitem.to_string().c_str());
switch (can_id) {
case cbf_sthome::CB_CONTROLLER_STATES:
{
uint8_t alarms = x[0];
uint8_t states = x[1];
uint8_t modes = x[2];
id(update_heating_display).execute(states & 0x80 != 0);
//if(states & 0x40 == 0) {
// lv_obj_add_flag(id(ind_geyser_relay_on), LV_OBJ_FLAG_HIDDEN);
//}
//else {
// lv_obj_clear_flag(id(ind_geyser_relay_on), LV_OBJ_FLAG_HIDDEN);
//}
if(states & 0x20 == 0) {
lv_obj_add_flag(id(ind_utility_on), LV_OBJ_FLAG_HIDDEN);
}
else {
lv_obj_clear_flag(id(ind_utility_on), LV_OBJ_FLAG_HIDDEN);
}
}
break;
case cbf_sthome::CB_GEYSER_TEMPERATURE_TOP:
{
double temperature = (x[1] == 0xFF && x[0] == 0xFF) ? std::numeric_limits<double>::quiet_NaN() : (double) static_cast<int16_t>(256 * x[1] + x[0]) / 256;
if(!isnan(temperature)) {
id(update_temp_display).execute(temperature, rect_gtoptemp, ind_utility_on, lbl_gtoptemp);
}
}
break;
case cbf_sthome::CB_GEYSER_TEMPERATURE_BOTTOM:
{
double temperature = (x[1] == 0xFF && x[0] == 0xFF) ? std::numeric_limits<double>::quiet_NaN() : (double) static_cast<int16_t>(256 * x[1] + x[0]) / 256;
if(!isnan(temperature)) {
id(update_temp_display).execute(temperature, rect_gbottemp, ind_geyser_heating_on, lbl_gbottemp);
}
}
break;
}
}
}
else {
ESP_LOGI("WARN", "CAN ID within unhandled range: 0x%X", can_id);
}
}
display:
- platform: ili9xxx
model: ili9488
id: tft_display
color_palette: 8BIT
data_rate: 40MHz
spi_id: spi_bus1
cs_pin: GPIO41
dc_pin: GPIO39
reset_pin: GPIO40
auto_clear_enabled: false
update_interval: never
invert_colors: false
# show_test_card: true
transform:
swap_xy: true # landscape
# mirror_x: true # landscape
dimensions:
height: 480
width: 320
# Define a PWM output on the ESP32
output:
- platform: ledc
pin: GPIO38
id: backlight_pwm
# Define a monochromatic, dimmable light for the backlight
light:
- platform: monochromatic
output: backlight_pwm
name: "Display Backlight"
id: back_light
restore_mode: ALWAYS_ON
touchscreen:
platform: xpt2046
id: touch_screen
spi_id: spi_bus1
cs_pin: GPIO47
interrupt_pin: GPIO19
transform:
swap_xy: true # landscape
# mirror_y: true # portrait
calibration:
x_min: 231 #201 #281
x_max: 3878 #3793 #3848
y_min: 221 #228 #347
y_max: 3861 #3914 #3878
lvgl:
# color_depth: 16
# bg_color: 0x0F0F0F
default_font: unscii_8
# align: center
theme:
button:
bg_color: grey_light #0x2F8CD8
# bg_grad_color: 0x005782
# bg_grad_dir: VER
bg_opa: COVER
border_color: 0x0077b3
border_width: 1
text_color: 0xFFFFFF
pressed: # set some button colors to be different in pressed state
bg_color: 0x006699
bg_grad_color: 0x00334d
checked: # set some button colors to be different in checked state
bg_color: 0x1d5f96
bg_grad_color: 0x03324A
text_color: 0xfff300
# switch:
# bg_color: 0xC0C0C0
# bg_grad_color: 0xb0b0b0
# bg_grad_dir: VER
# bg_opa: COVER
# checked:
# bg_color: 0x1d5f96
# bg_grad_color: 0x03324A
# bg_grad_dir: VER
# bg_opa: COVER
# knob:
# bg_color: 0xFFFFFF
# bg_grad_color: 0xC0C0C0
# bg_grad_dir: VER
# bg_opa: COVER
# slider:
# border_width: 1
# border_opa: 15%
# bg_color: 0xcccaca
# bg_opa: 15%
# indicator:
# bg_color: 0x1d5f96
# bg_grad_color: 0x03324A
# bg_grad_dir: VER
# bg_opa: COVER
# knob:
# bg_color: 0x2F8CD8
# bg_grad_color: 0x005782
# bg_grad_dir: VER
# bg_opa: COVER
# border_color: 0x0077b3
# border_width: 1
# text_color: 0xFFFFFF
style_definitions:
- id: header_footer
bg_color: darkgrey #0x2F8CD8
bg_opa: COVER
border_opa: TRANSP
radius: 0
pad_all: 0
pad_row: 0
pad_column: 0
border_color: 0x0077b3
text_color: 0xFFFFFF
width: 100%
height: 30
- id: clockdate_style
text_font: montserrat_20 #roboto_20 #unscii_8
text_align: center
text_color: 0x000000
radius: 4
pad_all: 2
- id: sty_calendar_small
radius: 0
pad_all: 0
pad_row: 0
pad_column: 0
text_font: unscii_8
shadow_opa: TRANSP
text_color: black
bg_color: white
bg_opa: COVER
border_color: grey_light
border_width: 1
border_opa: cover #TRANSP
- id: sty_calendar_small_noborders
radius: 0
pad_all: 0
pad_row: 0
pad_column: 0
text_font: unscii_8
shadow_opa: TRANSP
text_color: black
bg_color: white
bg_opa: COVER
border_color: grey_light
border_width: 0
border_opa: cover #TRANSP
displays:
- tft_display
buffer_size: 12%
top_layer:
widgets:
- label:
text: "\U0000F5A9" # "\uF1EB"
id: lbl_hastatus
hidden: true
align: top_right
x: -2
y: 1
text_font: font_icon_small #montserrat_16
text_align: right
text_color: 0x202020 # 0xFFFFFF
- obj: # clipping rectangle
x: 0 #15
y: -24 #7
pad_all: 0
height: 90
width: 65
align: BOTTOM_RIGHT
bg_color: 0x000000
border_color: 0xFFFFFF
border_width: 0
radius: 0
bg_opa: LV_OPA_TRANSP
scrollbar_mode: "OFF"
widgets:
- image:
id: ind_geyser_heating_on
align: CENTER #BOTTOM_RIGHT #TOP_RIGHT
src: icon_fire
image_recolor: RED
image_recolor_opa: 100%
x: 0 #15 #15
y: 0 #-22 #7
height: 100 #25
width: 100 #25
- obj: # clipping rectangle
x: 0 #15
y: 2 #-24 #7
pad_all: 0
height: 80
width: 65
align: TOP_LEFT
bg_color: 0x000000
border_color: 0xFFFFFF
border_width: 0
radius: 0
bg_opa: LV_OPA_TRANSP
scrollbar_mode: "OFF"
widgets:
- image:
id: ind_utility_on
align: CENTER #BOTTOM_RIGHT #TOP_RIGHT
src: icon_utility
image_recolor: grey #!lambda 'return lv_color_hex(0x000000);'
image_recolor_opa: 100%
x: 0 #15 #15
y: 0 #-22 #7
height: 80 #25
width: 80 #25
# - obj:
# id: boot_screen
# x: 0
# y: 0
# width: 100%
# height: 100%
# bg_color: 0xffffff
# bg_opa: COVER
# radius: 0
# pad_all: 0
# border_width: 0
# widgets:
# - image:
# align: CENTER
# src: boot_logo
# y: -40
# - spinner:
# align: CENTER
# y: 95
# height: 50
# width: 50
# spin_time: 1s
# arc_length: 60deg
# arc_width: 8
# indicator:
# arc_color: 0x18bcf2
# arc_width: 8
# on_press:
# - lvgl.widget.hide: boot_screen
- buttonmatrix:
text_font: montserrat_16
align: bottom_mid
styles: header_footer
pad_all: 0
outline_width: 0
id: footer
width: 480
items:
styles: header_footer
rows:
- buttons:
- id: page_prev
text: "\uF053"
on_press:
then:
lvgl.page.previous:
- id: page_home
text: "\uF015"
on_press:
then:
lvgl.page.show: main_page
- id: page_next
text: "\uF054"
on_press:
then:
lvgl.page.next:
pages:
# - id: pg_calendar
# widgets:
# - button:
# id: cal_btn_prev_month
# styles: sty_calendar_small
# align: TOP_MID
# pad_all: 0
# outline_width: 0
# border_color: black
# border_width: 0 #1
# border_opa: TRANSP
# x: -75
# y: 30
# width: 20
# height: 20
# bg_color: grey_light
# text_color: 0xD3D3D3
# text_font: montserrat_14
# widgets:
# - label:
# align: center
# text_font: montserrat_14
# text: "<"
# on_press:
# then:
# lambda: |-
# id(update_calendar_month).execute(-1);
# - dropdown:
# id: cal_dd_year
# styles: sty_calendar_small
# text_font: montserrat_12
# height: 20
# width: 55
# radius: 0
# align_to:
# id: cal_btn_prev_month
# align: out_right_top
# x: 80
# y: 0 #12.5%
# options:
# - 2024
# - 2025
# selected_index: 0
# dropdown_list:
# text_line_space: 3
# pad_all: 1
# text_font: unscii_8
# max_height: 260
# radius: 0
# selected:
# checked:
# text_color: 0xFF0000
# on_value:
# then:
# - lambda: |-
# id(update_calendar).execute();
# - dropdown:
# id: cal_dd_month
# styles: sty_calendar_small
# text_font: montserrat_12
# height: 20
# width: 55
# radius: 0
# align_to:
# id: cal_dd_year
# align: out_right_top
# x: 0
# y: 0 #12.5%
# options:
# - Jan
# - Feb
# - Mar
# - Apr
# - May
# - Jun
# - Jul
# - Aug
# - Sep
# - Oct
# - Nov
# - Dec
# selected_index: 0
# dropdown_list:
# text_line_space: 3
# pad_all: 1
# text_font: unscii_8
# max_height: 260
# radius: 0
# selected:
# checked:
# text_color: 0xFF0000
# on_value:
# then:
# - lambda: |-
# id(update_calendar).execute();
# - button:
# id: cal_btn_next_month
# styles: sty_calendar_small
# align_to:
# id: cal_dd_month
# align: out_right_top
# x: 0
# y: 0
# pad_all: 0
# outline_width: 0
# border_color: black
# border_width: 0 #1
# border_opa: TRANSP
# x: -75
# y: 30
# width: 20
# height: 20
# bg_color: grey_light
# text_color: 0xD3D3D3
# text_font: montserrat_14
# widgets:
# - label:
# align: center
# text_font: montserrat_14
# text: ">"
# on_press:
# then:
# lambda: |-
# id(update_calendar_month).execute(1);
# - buttonmatrix:
# id: bmx_cal_header_dow
# styles: sty_calendar_small_noborders
# align_to:
# id: cal_btn_prev_month
# align: out_bottom_left
# x: 80
# y: 0 #12.5%
# pad_all: 0
# outline_width: 0
# border_color: black
# border_width: 0 #1
# border_opa: TRANSP
# x: 0
# y: 0
# width: 150
# height: 20
# bg_color: black
# text_color: 0xD3D3D3
# items:
# styles: sty_calendar_small_noborders
# pressed:
# bg_color: 0x006699
# bg_grad_color: 0x00334d
# checked:
# bg_color: 0x1d5f96
# bg_grad_color: 0x03324A
# rows:
# - buttons:
# - id: r0c1
# text: "Su"
# width: 1
# - id: r0c2
# text: "Mo"
# width: 1
# - id: r0c3
# text: "Tu"
# width: 1
# - id: r0c4
# text: "We"
# width: 1
# - id: r0c5
# text: "Th"
# width: 1
# - id: r0c6
# text: "Fr"
# width: 1
# - id: r0c7
# text: "Sa"
# width: 1
# on_press:
# then:
# - lambda: |-
# ESP_LOGI("day of week", "%d", x);
# - buttonmatrix:
# id: bmx_calendar
# styles: sty_calendar_small
# align_to:
# id: bmx_cal_header_dow
# align: out_bottom_left
# x: 0
# y: 0 #12.5%
# pad_all: 0
# outline_width: 0
# border_color: black
# border_width: 0 #1
# border_opa: TRANSP
# x: 0
# y: 0
# width: 150
# height: 100
# bg_color: black
# text_color: 0xD3D3D3
# items:
# styles: sty_calendar_small
# pressed:
# bg_color: 0x006699
# bg_grad_color: 0x00334d
# checked:
# bg_color: 0x1d5f96
# bg_grad_color: 0x03324A
# rows:
# - buttons:
# - id: r1c1
# text: " "
# width: 1
# control:
# recolor: true
# - id: r1c2
# text: " "
# width: 1
# - id: r1c3
# text: "1"
# width: 1
# - id: r1c4
# text: "2"
# width: 1
# - id: r1c5
# text: "3"
# width: 1
# - id: r1c6
# text: "4"
# width: 1
# - id: r1c7
# text: "5"
# width: 1
# # Define actions on button press
# on_press:
# then:
# lambda: |-
# //lv_btnmatrix_set_btn_ctrl_all(bmx_calendar->obj, LV_BTNMATRIX_CTRL_CHECKABLE | LV_BTNMATRIX_CTRL_RECOLOR);
# //lv_btnmatrix_set_one_checked(bmx_calendar->obj, false);
# //id(get_calendar_days_state).execute("P1");
# //auto stat = lv_btnmatrix_has_btn_ctrl(bmx_calendar->obj, x, LV_BTNMATRIX_CTRL_CHECKED);
# //ESP_LOGI("on press", "day: %s, stat: %d", lv_btnmatrix_get_btn_text(bmx_calendar->obj, x), stat);
# //id(get_calendar_days_state).execute("P2");
# on_release:
# then:
# lambda: |-
# id(get_calendar_days_state).execute("R1");
# //auto stat = lv_btnmatrix_has_btn_ctrl(bmx_calendar->obj, x, LV_BTNMATRIX_CTRL_CHECKED);
# //auto* day = lv_btnmatrix_get_btn_text(bmx_calendar->obj, x);
# // if(stat) {
# // lv_btnmatrix_clear_btn_ctrl(bmx_calendar->obj, x, LV_BTNMATRIX_CTRL_CHECKED);
# // }
# // else {
# // lv_btnmatrix_set_btn_ctrl(bmx_calendar->obj, x, LV_BTNMATRIX_CTRL_CHECKED);
# // }
# //auto stat = lv_btnmatrix_has_btn_ctrl(bmx_calendar->obj, x, LV_BTNMATRIX_CTRL_CHECKED);
# //ESP_LOGI("on relse", "day: %s, stat: %d", lv_btnmatrix_get_btn_text(bmx_calendar->obj, x), stat);
# //id(get_calendar_days_state).execute("R2");
- id: main_page #pg_geyser_temp
widgets:
- obj:
id: rect_gtoptemp
x: 0
y: 0 #30
pad_all: 0
height: 290
width: 240
align: TOP_LEFT
bg_color: 0x000000
border_color: 0xFFFFFF
border_width: 0
radius: 0
bg_opa: COVER
- obj:
id: rect_gbottemp
y: 0
pad_all: 0
height: 290
width: 240
align_to:
id: rect_gtoptemp
align: out_right_top
x: 0
y: 0 #12.5%
bg_color: 0x000000 #0xFF4500
border_color: 0xFFFFFF
border_width: 0
radius: 0
bg_opa: COVER
- label:
text: " "
id: lbl_gtoptemp
hidden: false
align: LEFT_MID
x: 0
y: -10
text_font: roboto_200
text_align: center
text_color: 0x0
bg_opa: LV_OPA_TRANSP
bg_color: 0xffffff
- label:
text: " "
id: lbl_gbottemp
hidden: false
align: RIGHT_MID
x: 0
y: -10
text_font: roboto_200
text_align: center
text_color: 0x0
bg_opa: LV_OPA_TRANSP
bg_color: 0xffffff
# - label:
# text: "°C"
# id: lbl_degree
# hidden: false
# align: BOTTOM_MID
# x: 0
# y: -30
# text_font: geyser_temperature_font2
# text_align: center
# text_color: 0x0
# bg_opa: LV_OPA_TRANSP
# bg_color: 0xffffff
- label:
text: "top"
id: lbl_top
hidden: false
align: TOP_MID
x: -120
y: 20
text_font: geyser_temperature_font3
text_align: center
text_color: 0x0
bg_opa: LV_OPA_TRANSP
bg_color: 0xffffff
- label:
text: "bottom"
id: lbl_bottom
hidden: false
align: TOP_MID
x: 120
y: 20
text_font: geyser_temperature_font3
text_align: center
text_color: 0x0
bg_opa: LV_OPA_TRANSP
bg_color: 0xffffff
# - id: pg_settings
# widgets:
# - textarea:
# id: geyser_schedule
# one_line: true
# placeholder_text: "Enter text here"
# - keyboard:
# id: keyboard_id
# textarea: geyser_schedule
# mode: TEXT_UPPER
# text_font: montserrat_20
# on_focus:
# then:
# - lvgl.keyboard.update:
# id: keyboard_id
# mode: number
# textarea: geyser_schedule
# on_ready:
# then:
# - logger.log: Keyboard is ready
# on_cancel:
# then:
# - logger.log: Keyboard cancelled#
# - id: pg_clock
# widgets:
# - obj: # clock container
# height: 300 #SIZE_CONTENT
# width: 300 # 100%
# align: TOP_MID
# pad_all: 0
# border_width: 0
# bg_color: 0xFFFFFF
# widgets:
# - meter: # clock face
# height: 300
# width: 300
# align: TOP_MID
# bg_opa: TRANSP
# border_width: 0
# text_color: 0x000000
# scales:
# - range_from: 0 # minutes scale
# range_to: 720
# angle_range: 360
# rotation: 270
# ticks:
# width: 1
# count: 61
# length: 10
# color: 0x000000
# indicators:
# - line:
# id: minute_hand
# width: 3
# color: 0xa6a6a6
# r_mod: -4
# value: 0
# - range_from: 1 # hours scale for labels
# range_to: 12
# angle_range: 330
# rotation: 300
# ticks:
# width: 1
# count: 12
# length: 1
# major:
# stride: 1
# width: 4
# length: 10
# color: 0xC0C0C0
# label_gap: 12
# - range_from: 0 # hi-res hours scale for hand
# range_to: 720
# angle_range: 360
# rotation: 270
# ticks:
# count: 0
# indicators:
# - line:
# id: hour_hand
# width: 5
# color: 0xa6a6a6
# r_mod: -30
# value: 0
# # Second hand
# - angle_range: 360
# rotation: 270
# range_from: 0
# range_to: 60
# indicators:
# - line:
# id: second_hand
# width: 2
# color: Red
# r_mod: -10
# - label:
# align: CENTER
# styles: clockdate_style
# id: day_label
# y: -50
# - label:
# align: CENTER
# id: date_label
# styles: clockdate_style
# y: 50
# - id: pg_digital_clock
# widgets:
# - obj:
# id: rect_gtoptemp1
# x: 0
# y: 0 #30
# pad_all: 0
# height: 290
# width: 240
# align: TOP_LEFT
# bg_color: 0x000000
# border_color: 0xFFFFFF
# border_width: 0
# radius: 0
# bg_opa: COVER
# - obj:
# id: rect_gbottemp1
# y: 0
# pad_all: 0
# height: 290
# width: 240
# align_to:
# id: rect_gtoptemp
# align: out_right_top
# x: 0
# y: 0 #12.5%
# bg_color: 0x000000 #0xFF4500
# border_color: 0xFFFFFF
# border_width: 0
# radius: 0
# bg_opa: COVER
# - label:
# text: " "
# id: lbl_digitalclock
# hidden: false
# align: TOP_MID
# x: 0
# y: 20
# text_font: roboto_192
# text_align: center
# text_color: RED
# bg_opa: LV_OPA_TRANSP
# bg_color: 0xffffff
switch:
- platform: restart
name: "${name} Restart"
id: "restart_switch"
# - platform: template
# name: Antiburn
# id: switch_antiburn
# icon: mdi:television-shimmer
# optimistic: true
# entity_category: "config"
# turn_on_action:
# - logger.log: "Starting Antiburn"
# - if:
# condition: lvgl.is_paused
# then:
# - lvgl.resume:
# - lvgl.widget.redraw:
# - lvgl.pause:
# show_snow: true
# turn_off_action:
# - logger.log: "Stopping Antiburn"
# - if:
# condition: lvgl.is_paused
# then:
# - lvgl.resume:
# - lvgl.widget.redraw:
sensor:
# - platform: dallas_temp
# address: 0xfe00000037b3d528
# name: "Study Temperature"
# id: study_temperature
# update_interval: "60s"
# resolution: 12
# one_wire_id: temperature_sensors
# unit_of_measurement: "°C"
# #icon: "mdi:water-thermometer"
# device_class: "temperature"
# state_class: "measurement"
# accuracy_decimals: 1
# filters:
# - filter_out: nan
# # - sliding_window_moving_average:
# # window_size: 120 # averages over 120 update intervals
# # send_every: 60 # reports every 60 update intervals
# Report wifi signal strength every 5 min if changed
- platform: wifi_signal
name: WiFi Signal
id: wifi_sig
update_interval: 300s
filters:
- delta: 10%
# human readable uptime sensor output to the text sensor above
# - platform: uptime
# name: Uptime in Days
# id: uptime_sensor_days
# update_interval: 10s
# on_raw_value:
# then:
# - text_sensor.template.publish:
# id: uptime_human
# state: !lambda |-
# int seconds = round(id(uptime_sensor_days).raw_state);
# int days = seconds / (24 * 3600);
# seconds = seconds % (24 * 3600);
# int hours = seconds / 3600;
# seconds = seconds % 3600;
# int minutes = seconds / 60;
# seconds = seconds % 60;
# auto days_str = std::to_string(days);
# auto hours_str = std::to_string(hours);
# auto minutes_str = std::to_string(minutes);
# auto seconds_str = std::to_string(seconds);
# return (
# (days ? days_str + "d " : "") +
# (hours ? hours_str + "h " : "") +
# (minutes ? minutes_str + "m " : "") +
# (seconds_str + "s")
# ).c_str();
#
## number of seconds since midnight
# - platform: template
# id: time_of_day
# name: "Time of day"
# accuracy_decimals: 0
# unit_of_measurement: "s"
# lambda: |-
# auto currenttime = id(time_source).now();
# ESPTime time_obj = currenttime;
# time_obj.second = 0;
# time_obj.minute = 0;
# time_obj.hour = 0;
# time_obj.recalc_timestamp_local();
# return currenttime.timestamp - time_obj.timestamp;
# update_interval: 60s
#text_sensor:
## - platform: template
## id: module_time
## name: "Module time"
## icon: mdi:clock
## lambda: |-
## auto time_obj = id(time_source).now();
## return time_obj.strftime("%Y-%m-%d %H:%M:%S");
## update_interval: 60s
#
# # Expose WiFi information as sensors
# - platform: wifi_info
# ip_address:
# name: IP
# mac_address:
# name: Mac Address
# entity_category: diagnostic
# ssid:
# name: "Connected SSID"
# id: ssid
# entity_category: diagnostic
#
# # human readable update text sensor from sensor:uptime
# - platform: template
# name: Uptime
# id: uptime_human
# icon: mdi:clock-start
#
# - platform: template
# name: 'Last Restart'
# id: device_last_restart
# icon: mdi:clock
# entity_category: diagnostic
#
script:
- id: send_quick_update_request
then:
- lambda: |-
for(int i = 0; i < ${CB_MAX_RETRANSMISSIONS}; i++) {
id(send_request_block1).execute();
}
- id: send_info_request
then:
- lambda: |-
for(int i = 0; i < ${CB_MAX_RETRANSMISSIONS}; i++) {
id(send_request_block2).execute();
}
- id: send_request_block1
mode: queued
then:
- lambda: "id(g_cb_cache).send_request(id(canbus_sthome), solar::cbf_sthome::CB_CONTROLLER_STATES);"
- delay: 20ms
- id: send_request_block2
mode: queued
then:
- lambda: "id(g_cb_cache).send_request(id(canbus_sthome), solar::cbf_pylon::CB_BATTERY_STATE);"
- delay: 50ms
- lambda: "id(g_cb_cache).send_request(id(canbus_sthome), solar::cbf_sthome::CB_GEYSER_TEMPERATURE_TOP);"
- delay: 50ms
- lambda: "id(g_cb_cache).send_request(id(canbus_sthome), solar::cbf_sthome::CB_GEYSER_TEMPERATURE_BOTTOM);"
- delay: 50ms
- id: update_temp_display
mode: queued
parameters:
value: double
rect: lv_obj_t*
indicator: lv_obj_t*
label: lv_obj_t*
then:
- lambda: |-
char buffer [4];
buffer[0] = '\0';
snprintf (buffer, 4, "%.0f", value);
auto bgcolor = lv_color_hex(0xFF0000);
auto ind_color = lv_color_hex(0xFF0000);
if(value < 40) {
bgcolor = lv_color_hex(0x0000FF);
}
else if(value < 50) {
bgcolor = lv_color_hex(0x00FF00);
}
else if(value < 60) {
bgcolor = lv_color_hex(0xFFFF00);
}
else {
ind_color = lv_color_hex(0xFFFF00); // make different to bgcolor
}
lv_obj_set_style_bg_color(rect, bgcolor, LV_PART_MAIN);
lv_obj_set_style_img_recolor(indicator, ind_color, LV_PART_MAIN);
if(isnan(value))
lv_label_set_text(label, "??");
else
lv_label_set_text(label, buffer);
- id: update_heating_display
parameters:
heat_on: bool
then:
- lvgl.widget.update:
id: ind_geyser_heating_on
hidden: !lambda 'return !heat_on;'
# - id: time_update
# then:
# - lvgl.indicator.update:
# id: minute_hand
# value: !lambda |-
# auto now = id(time_source).now();
# return now.minute * 12 + now.second/5;
# - lvgl.indicator.update:
# id: hour_hand
# value: !lambda |-
# auto now = id(time_source).now();
# return std::fmod(now.hour, 12) * 60 + now.minute;
# - lvgl.indicator.update:
# id: second_hand
# value: !lambda |-
# auto now = id(time_source).now();
# return now.second;
# - lvgl.label.update:
# id: date_label
# text: !lambda |-
# static const char * const mon_names[] = {"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
# static char date_buf[8];
# auto now = id(time_source).now();
# snprintf(date_buf, sizeof(date_buf), "%s %2d", mon_names[now.month-1], now.day_of_month);
# return date_buf;
# - lvgl.label.update:
# id: day_label
# text: !lambda |-
# static const char * const day_names[] = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"};
# return day_names[id(time_source).now().day_of_week - 1];
# - lvgl.label.update:
# id: lbl_digitalclock
# text: !lambda |-
# auto time_obj = id(time_source).now();
# return time_obj.strftime("%H:%M");
# - id: init_calendar
# then:
# - lambda: |-
# auto now = id(time_source).now();
# //ESP_LOGI("yopts before", stroptions.c_str());
# int y = 0;
# std::string stroptions = to_string(now.year + y);
# while(++y < ${DD_MAX_YEARS}) {
# stroptions += "\n" + to_string(now.year + y);
# }
# //ESP_LOGI("yopts after", stroptions.c_str());
# lv_dropdown_set_options(cal_dd_year->obj, stroptions.c_str());
# lv_dropdown_set_selected(cal_dd_year->obj, 0); // this year is first index
# lv_dropdown_set_selected(cal_dd_month->obj, now.month-1);
# id(g_year_idx) = 0;
# id(update_calendar).execute();
# lv_btnmatrix_set_btn_ctrl_all(bmx_calendar->obj, LV_BTNMATRIX_CTRL_CHECKABLE | LV_BTNMATRIX_CTRL_RECOLOR);
# - id: update_calendar_month
# parameters:
# increment : int
# then:
# - lambda: |-
# char yearstr[8];
# lv_dropdown_get_selected_str(cal_dd_year->obj, yearstr, sizeof(yearstr));
# auto year = atoi(yearstr);
# int year_idx = lv_dropdown_get_selected(cal_dd_year->obj);
# int month_idx = increment + lv_dropdown_get_selected(cal_dd_month->obj);
# int month = 1 + month_idx;
# if(month > 12 && year_idx < ${DD_MAX_YEARS} - 1) {
# month -= 12;
# month_idx -= 12;
# year++;
# year_idx++;
# }
# else if(month < 1 && year_idx > 0) {
# month += 12;
# month_idx += 12;
# year--;
# year_idx--;
# }
# ESP_LOGI("cm", "month: %d, year: %d", month, year);
# if(month < 13 && month > 0) {
# lv_dropdown_set_selected(cal_dd_year->obj, year_idx);
# lv_dropdown_set_selected(cal_dd_month->obj, month_idx);
# id(g_year_idx) = year_idx;
# id(update_calendar).execute();
# }
#
# - id: update_calendar
# then:
# lambda: |-
# char yearstr[8];
# int monthdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
# static std::string strdays[44];
# static const char *pstrdays[49]; // including newline at end of week
# static const char *newline = "\n";
# id(persist_calendar).execute();
# lv_dropdown_get_selected_str(cal_dd_year->obj, yearstr, sizeof(yearstr));
# int year = atoi(yearstr);
# int month = 1 + lv_dropdown_get_selected(cal_dd_month->obj);
# bool isLeapYear = (year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0));
# monthdays[1] = (isLeapYear) ? 29 : 28;
# // calculate day of week of 1st of month using Zeller's rule
# // https://beginnersbook.com/2013/04/calculating-day-given-date
# // modified month, year
# int mM = month - 2;
# int m = mM < 1 ? 12 + mM : mM;
# int mY = mM < 1 ? year - 1 : year;
# int k = 1; // day of month
# int D = mY % 100; // last two digits of the year
# int C = trunc(mY / 100); // first two digits of the year
# int F = k + trunc((13 * m - 1) / 5) + D + trunc(D / 4) + trunc(C / 4) - 2 * C;
# int Z = F % 7;
# int start_of_month = Z < 0 ? Z + 7 : Z;
# // end of Zeller's rule
# int previous_month = (month == 1) ? 12 : month - 1;
# int month_days = monthdays[month - 1];
# int prev_month_days = monthdays[previous_month - 1];
# int i = 0;
# int j = -1;
# //ESP_LOGI("vals", "start_of_month: %d, previous_month: %d, month_days: %d, prev_month_days: %d", start_of_month, previous_month, month_days, prev_month_days);
# for (int w = 0; w < 6 && i < (month_days + start_of_month); w++) {
# for(int wd = 0; wd < 7; wd++) {
# int day = i + 1 - start_of_month;
# if (i < start_of_month) {
# day += prev_month_days;
# strdays[i] = "#e0e0e0 " + to_string(day) + "#";
# }
# else if (i >= (month_days + start_of_month)) {
# day -= month_days;
# strdays[i] = "#e0e0e0 " + to_string(day) + "#";
# }
# else {
# strdays[i] = to_string(day);
# }
# pstrdays[++j] = strdays[i].c_str();
# //ESP_LOGI("bmx", "%s, i: %d, j: %d", pstrdays[j], i, j);
# i++;
# }
# pstrdays[++j] = newline;
# //ESP_LOGI("bmxnl", "%s, i: %d, j: %d", pstrdays[j], i, j);
# }
# pstrdays[j] = NULL; // terminator, overwrites last newline
# //ESP_LOGW("day", "terminating at: %d", j);
# lv_btnmatrix_set_btn_ctrl_all(bmx_calendar->obj, LV_BTNMATRIX_CTRL_CHECKABLE | LV_BTNMATRIX_CTRL_RECOLOR);
# lv_btnmatrix_set_map(bmx_calendar->obj, pstrdays);
# lv_btnmatrix_set_btn_ctrl_all(bmx_calendar->obj, LV_BTNMATRIX_CTRL_CHECKABLE | LV_BTNMATRIX_CTRL_RECOLOR);
#
# - id: persist_calendar
# then:
# lambda: |-
# id(g_year_idx) = lv_dropdown_get_selected(cal_dd_year->obj);
# id(g_month_idx) = lv_dropdown_get_selected(cal_dd_month->obj);
# // copy year options to persistent globals
# const char* opts = lv_dropdown_get_options(cal_dd_year->obj);
# int opt_store_size = sizeof(id(g_options_year));
# strncpy(id(g_options_year), opts, opt_store_size);
# id(g_options_year)[opt_store_size] = '\0';
# //ESP_LOGI("year options", id(g_options_year));
#
# - id: get_calendar_days_state
# parameters:
# flag: std::string
# then:
# lambda: |-
# // count buttons
# int num_buttons = 0;
# auto* buttonmap = lv_btnmatrix_get_map(bmx_calendar->obj);
# int i = 0;
# for (; buttonmap[i] != NULL && buttonmap[i][0] != '\0' && i < 48; i++) {
# bool isNewLine = strcmp(buttonmap[i], "\n") == 0;
# if (!isNewLine) {
# num_buttons++;
# }
# }
# std::string sch_holidays = "";
# std::string pub_holidays = "";
# std::string vac_days = "";
# int h = 0;
# for(int i = 0; i < num_buttons; i++) {
# bool isSchoolHoliday = lv_btnmatrix_has_btn_ctrl(bmx_calendar->obj, i, LV_BTNMATRIX_CTRL_CHECKED);
# bool isPublicHoliday = false; //lv_btnmatrix_has_btn_ctrl(bmx_calendar->obj, i, LV_BTNMATRIX_CTRL_CUSTOM_1);
# bool isVacationDay = false; //lv_btnmatrix_has_btn_ctrl(bmx_calendar->obj, i, LV_BTNMATRIX_CTRL_CUSTOM_2);
# if(isSchoolHoliday || isPublicHoliday || isVacationDay) {
# sch_holidays = sch_holidays + lv_btnmatrix_get_btn_text(bmx_calendar->obj, i) + " ";
# h++;
# }
# }
# if(h > 0) {
# ESP_LOGI("day", "[%s] s: %s \tp: %s \tv: %s", flag.c_str(), sch_holidays.c_str(), pub_holidays.c_str(), vac_days.c_str());
# }