1489 lines
46 KiB
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());
|
|
# }
|
|
|
|
|