1529 lines
43 KiB
YAML
1529 lines
43 KiB
YAML
substitutions:
|
|
name: sthome-ut9
|
|
friendly_name: "sthome-ut9"
|
|
#ALLOWED_CHARACTERS_FULL: " !#%\"'()+,-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWYZ[]_abcdefghijklmnopqrstuvwxyz{|}°²³µ¿ÁÂÄÅÉÖÚßàáâãäåæçèéêëìíîðñòóôõöøùúûüýþāăąćčďĐđēėęěğĮįıļľŁłńňőřśšťũūůűųźŻżŽžơưșțΆΈΌΐΑΒΓΔΕΖΗΘΚΜΝΠΡΣΤΥΦάέήίαβγδεζηθικλμνξοπρςστυφχψωϊόύώАБВГДЕЖЗИКЛМНОПРСТУХЦЧШЪЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяёђєіїјљњћ"
|
|
ALLOWED_CHARACTERS: " !#%\"'()+,-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWYZ[]_abcdefghijklmnopqrstuvwxyz{|}°²³µ•"
|
|
DD_MAX_YEARS: "5"
|
|
esphome:
|
|
name: "${name}"
|
|
friendly_name: "${friendly_name}"
|
|
on_boot:
|
|
- lambda: |-
|
|
char yearstr[8];
|
|
lv_dropdown_set_selected(cal_dd_year->obj, 0);
|
|
lv_dropdown_set_selected(cal_dd_month->obj, 0);
|
|
lv_dropdown_get_selected_str(cal_dd_year->obj, yearstr, sizeof(yearstr));
|
|
int year = atoi(yearstr);
|
|
id(update_calendar).execute(year, 1);
|
|
- lvgl.widget.hide: ind_geyser_on
|
|
- delay: 5s
|
|
- lvgl.widget.hide: boot_screen
|
|
|
|
|
|
esp32:
|
|
board: esp32dev
|
|
framework:
|
|
type: arduino
|
|
#type: esp-idf
|
|
|
|
#debug:
|
|
# update_interval: 5s
|
|
|
|
# Enable logging
|
|
logger:
|
|
level: info
|
|
|
|
# Enable Home Assistant API
|
|
api:
|
|
encryption:
|
|
key: "LI7j37zs9HsWNsUZ5c83leThmhHsgIVReAPoc9U6pVU="
|
|
on_client_connected:
|
|
- if:
|
|
condition:
|
|
lambda: 'return (0 == client_info.find("Home Assistant "));'
|
|
then:
|
|
- lvgl.widget.show: lbl_hastatus
|
|
- lvgl.widget.show: lbl_degree
|
|
on_client_disconnected:
|
|
- if:
|
|
condition:
|
|
lambda: 'return (0 == client_info.find("Home Assistant "));'
|
|
then:
|
|
- lvgl.widget.hide: lbl_hastatus
|
|
- lvgl.widget.hide: ind_geyser_on
|
|
- lvgl.widget.hide: lbl_degree
|
|
ota:
|
|
- platform: esphome
|
|
password: "8ebd5bcefbdc833a5f6ddc4e8ba56e39"
|
|
|
|
wifi:
|
|
#ssid: !secret wifi_ssid
|
|
#password: !secret wifi_password
|
|
# we will use local dns server for local dns resolution
|
|
power_save_mode: none
|
|
domain: ".sthome.org"
|
|
networks:
|
|
- ssid: !secret wifi_ssid1
|
|
password: !secret wifi_password1
|
|
- ssid: !secret wifi_ssid2
|
|
password: !secret wifi_password2
|
|
- ssid: !secret wifi_ssid3
|
|
password: !secret wifi_password3
|
|
- ssid: !secret wifi_ssid4
|
|
password: !secret wifi_password4
|
|
- ssid: !secret wifi_ssid5
|
|
password: !secret wifi_password5
|
|
manual_ip:
|
|
# For faster connection startup, set a static IP address
|
|
# Set this to the IP of the ESP
|
|
static_ip: 10.0.2.9
|
|
gateway: 10.0.0.2
|
|
subnet: 255.255.240.0
|
|
dns1: 10.0.0.1
|
|
dns2: 1.1.1.1
|
|
# Enable fallback hotspot (captive portal) in case wifi connection fails
|
|
ap:
|
|
ssid: "${name} Fallback Hotspot"
|
|
password: "iZxjpw7ucRs4"
|
|
|
|
captive_portal:
|
|
|
|
#psram:
|
|
|
|
one_wire:
|
|
- platform: gpio
|
|
pin: GPIO4
|
|
id: temperature_sensors
|
|
|
|
#i2c:
|
|
# sda: GPIO21
|
|
# scl: GPIO22
|
|
|
|
font:
|
|
# - file: "fonts/Windows/comic.ttf"
|
|
# id: comic
|
|
# size: 20
|
|
# bpp: 4 #2
|
|
|
|
# - file: "fonts/misc/tom-thumb.bdf"
|
|
# id: tomthumb
|
|
#
|
|
# # gfonts://family[@weight]
|
|
# - file: "gfonts://Roboto"
|
|
# id: roboto_20
|
|
# size: 20
|
|
#
|
|
# - file:
|
|
# type: gfonts
|
|
# family: Roboto
|
|
# weight: 900
|
|
# id: roboto_20
|
|
# size: 20
|
|
#
|
|
# - file: "fonts/Windows/arial.ttf"
|
|
# id: arial_14
|
|
# size: 14
|
|
# # gfonts://family[@weight]
|
|
# - file: "gfonts://Roboto"
|
|
# id: roboto
|
|
# size: 20
|
|
- file: "gfonts://Carrois Gothic"
|
|
id: Carrois_Gothic
|
|
size: 20
|
|
bpp: 4
|
|
- file: "gfonts://Roboto"
|
|
id: roboto_200
|
|
size: 200
|
|
bpp: 4
|
|
glyphs: [
|
|
0123456789,.,°,a,n,
|
|
"\u0020", # space
|
|
"\u003A", # colon
|
|
]
|
|
- file: "gfonts://Roboto"
|
|
id: roboto_192
|
|
size: 192
|
|
bpp: 4
|
|
glyphs: [
|
|
0123456789,.,°,a,n,
|
|
"\u0020", # space
|
|
"\u003A", # colon
|
|
]
|
|
- 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: "gfonts://Roboto"
|
|
id: roboto_100
|
|
size: 100
|
|
bpp: 4
|
|
glyphs: [
|
|
0123456789,.,a,n,
|
|
"\u0020", # space
|
|
"\u003A", # colon
|
|
]
|
|
# - file: "gfonts://Merriweather"
|
|
# id: Merriweather
|
|
# size: 30
|
|
#
|
|
# - file: "gfonts://Material+Symbols+Outlined"
|
|
# id: icons_50
|
|
# size: 50
|
|
# glyphs: ["\U0000e425"] # mdi-timer
|
|
#
|
|
# - file: "https://github.com/AlmostInteractive/ESP32-S3-Box-3-Voice-Assistant-Sensor-Dock/raw/master/fonts/materialdesignicons-webfont.ttf"
|
|
# id: font_icon_tiny
|
|
# size: 30
|
|
# glyphs: [
|
|
# "\U000F009A",#bell
|
|
# "\U000F009C",#bell-outline
|
|
# "\U000F00AD",#block-helper
|
|
# "\U000F01F2",#emoticon-outline
|
|
# "\U000F0375",#minus-box
|
|
# "\U000F0416",#plus-box
|
|
# ]
|
|
#
|
|
- file: "fonts/misc/materialdesignicons-webfont.ttf"
|
|
id: font_icon_small
|
|
size: 24 #45
|
|
glyphs: [
|
|
"\U0000F5A9",
|
|
]
|
|
# - file: "https://github.com/AlmostInteractive/ESP32-S3-Box-3-Voice-Assistant-Sensor-Dock/raw/master/fonts/materialdesignicons-webfont.ttf"
|
|
# id: font_icon_large
|
|
# size: 65
|
|
# glyphs: [
|
|
# "\U000F004F",#arrow-left-bold-circle
|
|
# "\U000F0056",#arrow-right-bold-circle
|
|
# ]
|
|
|
|
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 #RGB565
|
|
# - file: "images/fire.svg"
|
|
# id: img_fire
|
|
# resize: 100x100
|
|
# type: RGB565
|
|
# transparency: alpha_channel
|
|
# - file: "images/fire-red.svg"
|
|
# id: img_fire_red
|
|
# resize: 100x100 # 25x25
|
|
# type: RGB565
|
|
# transparency: alpha_channel
|
|
# - file: "images/thermometer2.png"
|
|
# id: thermometer_img
|
|
# type: RGB565
|
|
# transparency: alpha_channel
|
|
|
|
color:
|
|
- id: green
|
|
hex: '75D15F'
|
|
- id: red
|
|
hex: 'FF3131'
|
|
- id: light_red
|
|
hex: 'FF3340'
|
|
- id: blue
|
|
hex: '47B7E9'
|
|
- id: blue_light
|
|
hex: 'CFE2F3'
|
|
- id: blue_dark
|
|
hex: '085296'
|
|
- id: amber
|
|
hex: 'FBAB35'
|
|
- id: lime
|
|
hex: '20FC30'
|
|
- id: pink
|
|
hex: 'D92BBC'
|
|
- id: yellow
|
|
hex: 'FFC000'
|
|
- id: black
|
|
hex: '000000'
|
|
- id: white
|
|
hex: 'ffffff'
|
|
- id: purple
|
|
hex: '73264D'
|
|
- id: grey_dark
|
|
hex: '202020'
|
|
- id: grey_light
|
|
hex: 'e0e0e0'
|
|
|
|
|
|
sun:
|
|
id: sun_sensor
|
|
latitude: !secret latitude
|
|
longitude: !secret longitude
|
|
|
|
#time:
|
|
# - platform: sntp
|
|
# timezone: Africa/Johannesburg
|
|
# servers:
|
|
# - ntp1.meraka.csir.co.za # 146.64.24.58
|
|
# - ntp.as3741.net # 196.4.160.4
|
|
# - ntp1.inx.net.za # 196.10.52.57
|
|
time:
|
|
- platform: homeassistant
|
|
id: time_source
|
|
update_interval: 360min # Change sync interval from default 5min to 6 hours
|
|
on_time_sync:
|
|
then:
|
|
- 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: ind_heating_update
|
|
- script.execute: time_update
|
|
- script.execute: init_calendar
|
|
|
|
on_time:
|
|
- minutes: '*'
|
|
seconds: '*'
|
|
then:
|
|
- script.execute: ind_heating_update
|
|
- script.execute: time_update
|
|
|
|
- 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
|
|
|
|
spi:
|
|
- id: spi_bus0
|
|
clk_pin: GPIO18
|
|
mosi_pin: GPIO23
|
|
miso_pin: GPIO19
|
|
interface: any
|
|
|
|
display:
|
|
- platform: ili9xxx
|
|
model: ili9488
|
|
id: tft_display
|
|
color_palette: 8BIT
|
|
data_rate: 40MHz
|
|
cs_pin: GPIO15
|
|
dc_pin: GPIO2
|
|
reset_pin: GPIO27
|
|
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: GPIO26
|
|
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
|
|
cs_pin: GPIO33
|
|
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
|
|
# on_touch:
|
|
# - lambda: |-
|
|
# ESP_LOGI("cal", "x=%d, y=%d, x_raw=%d, y_raw=%0d",
|
|
# touch.x,
|
|
# touch.y,
|
|
# touch.x_raw,
|
|
# touch.y_raw
|
|
# );
|
|
# on_release:
|
|
# then:
|
|
# - if:
|
|
# condition: lvgl.is_paused
|
|
# then:
|
|
# - lvgl.resume:
|
|
# - lvgl.widget.redraw:
|
|
|
|
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: 20%
|
|
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_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:
|
|
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: |-
|
|
char yearstr[8];
|
|
lv_dropdown_get_selected_str(cal_dd_year->obj, yearstr, sizeof(yearstr));
|
|
int month = 1 + lv_dropdown_get_selected(cal_dd_month->obj);
|
|
auto year = atoi(yearstr);
|
|
id(update_calendar).execute(year, month);
|
|
- 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: |-
|
|
char yearstr[8];
|
|
lv_dropdown_get_selected_str(cal_dd_year->obj, yearstr, sizeof(yearstr));
|
|
auto year = atoi(yearstr);
|
|
id(update_calendar).execute(year, x + 1);
|
|
- 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("lvgl", "%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
|
|
# - buttons:
|
|
# - id: r2c1
|
|
# text: "6"
|
|
# width: 1
|
|
# control:
|
|
# recolor: true
|
|
# - id: r2c2
|
|
# text: "7"
|
|
# width: 1
|
|
# - id: r2c3
|
|
# text: "8"
|
|
# width: 1
|
|
# - id: r2c4
|
|
# text: "9"
|
|
# width: 1
|
|
# - id: r2c5
|
|
# text: "10"
|
|
# width: 1
|
|
# - id: r2c6
|
|
# text: "11"
|
|
# width: 1
|
|
# - id: r2c7
|
|
# text: "12"
|
|
# width: 1
|
|
# - buttons:
|
|
# - id: r3c1
|
|
# text: "13"
|
|
# width: 1
|
|
# - id: r3c2
|
|
# text: "14"
|
|
# width: 1
|
|
# - id: r3c3
|
|
# text: "15"
|
|
# width: 1
|
|
# - id: r3c4
|
|
# text: "16"
|
|
# width: 1
|
|
# - id: r3c5
|
|
# text: "17"
|
|
# width: 1
|
|
# - id: r3c6
|
|
# text: "18"
|
|
# width: 1
|
|
# - id: r3c7
|
|
# text: "19"
|
|
# width: 1
|
|
# - buttons:
|
|
# - id: r4c1
|
|
# text: "20"
|
|
# width: 1
|
|
# - id: r4c2
|
|
# text: "21"
|
|
# width: 1
|
|
# - id: r4c3
|
|
# text: "22"
|
|
# width: 1
|
|
# - id: r4c4
|
|
# text: "23"
|
|
# width: 1
|
|
# - id: r4c5
|
|
# text: "24"
|
|
# width: 1
|
|
# - id: r4c6
|
|
# text: "25"
|
|
# width: 1
|
|
# - id: r4c7
|
|
# text: "26"
|
|
# width: 1
|
|
# - buttons:
|
|
# - id: r5c1
|
|
# text: "27"
|
|
# width: 1
|
|
# - id: r5c2
|
|
# text: "28"
|
|
# width: 1
|
|
# - id: r5c3
|
|
# text: "29"
|
|
# width: 1
|
|
# - id: r5c4
|
|
# text: "30"
|
|
# width: 1
|
|
# - id: r5c5
|
|
# text: "31"
|
|
# width: 1
|
|
# - id: r5c6
|
|
# text: " "
|
|
# width: 1
|
|
# - id: r5c7
|
|
# text: " "
|
|
# width: 1
|
|
# Define actions on button press
|
|
on_press:
|
|
then:
|
|
lambda: |-
|
|
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);
|
|
ESP_LOGI("lvgl", "%d (%s) [%d]", x, day, stat);
|
|
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);
|
|
}
|
|
|
|
|
|
- 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
|
|
|
|
#interval:
|
|
# - interval: 30s
|
|
# then:
|
|
# - lvgl.page.next
|
|
|
|
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:
|
|
|
|
binary_sensor:
|
|
- platform: homeassistant
|
|
name: "Geyser Heating"
|
|
entity_id: binary_sensor.sthome_ut8_heating
|
|
id: geyser_heating
|
|
on_press:
|
|
then:
|
|
- lvgl.widget.show: ind_geyser_on
|
|
on_release:
|
|
then:
|
|
- lvgl.widget.hide: ind_geyser_on
|
|
|
|
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: 10s
|
|
|
|
- platform: homeassistant
|
|
name: "Geyser Top Temperature"
|
|
entity_id: sensor.sthome_ut8_geyser_top_temperature
|
|
id: geyser_top_temperature
|
|
on_raw_value:
|
|
then:
|
|
- lvgl.label.update:
|
|
id: lbl_gtoptemp
|
|
text: !lambda |-
|
|
if(isnan(x)) {
|
|
return ".";
|
|
}
|
|
else {
|
|
char buffer [10];
|
|
buffer[0] = '\0';
|
|
snprintf (buffer, 10, "%.0f", x);
|
|
return buffer;
|
|
}
|
|
- lvgl.widget.update:
|
|
id: rect_gtoptemp
|
|
bg_color: !lambda |-
|
|
if(isnan(x)) {
|
|
return lv_color_hex(0x000000);
|
|
}
|
|
if(x >= 60) {
|
|
return lv_color_hex(0xFF0000);
|
|
}
|
|
if(x >= 50) {
|
|
return lv_color_hex(0xFFFF00);
|
|
}
|
|
if(x >= 40) {
|
|
return lv_color_hex(0x00FF00);
|
|
}
|
|
return lv_color_hex(0x0000FF);
|
|
|
|
- platform: homeassistant
|
|
name: "Geyser Bottom Temperature"
|
|
entity_id: sensor.sthome_ut8_geyser_bottom_temperature
|
|
id: geyser_bottom_temperature
|
|
on_raw_value:
|
|
then:
|
|
- lvgl.label.update:
|
|
id: lbl_gbottemp
|
|
text: !lambda |-
|
|
if(isnan(x)) {
|
|
return ".";
|
|
}
|
|
else {
|
|
char buffer [10];
|
|
buffer[0] = '\0';
|
|
snprintf (buffer, 10, "%.0f", x);
|
|
return buffer;
|
|
}
|
|
- lvgl.widget.update:
|
|
id: rect_gbottemp
|
|
bg_color: !lambda |-
|
|
if(isnan(x)) {
|
|
return lv_color_hex(0x000000);
|
|
}
|
|
if(x < 40) {
|
|
return lv_color_hex(0x0000FF);
|
|
}
|
|
if(x < 50) {
|
|
return lv_color_hex(0x00FF00);
|
|
}
|
|
if(x < 60) {
|
|
return lv_color_hex(0xFFFF00);
|
|
}
|
|
return lv_color_hex(0xFF0000);
|
|
|
|
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: 1s
|
|
|
|
# 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: 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: ind_heating_update
|
|
then:
|
|
- lvgl.widget.update:
|
|
id: ind_geyser_on
|
|
hidden: !lambda return !id(geyser_heating).state;
|
|
|
|
- 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(update_calendar).execute(now.year, now.month);
|
|
|
|
- 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(update_calendar).execute(year, month);
|
|
}
|
|
|
|
- id: update_calendar
|
|
parameters:
|
|
year : int
|
|
month : int
|
|
then:
|
|
lambda: |-
|
|
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";
|
|
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
|
|
lv_btnmatrix_set_btn_ctrl_all(bmx_calendar->obj, LV_BTNMATRIX_CTRL_CHECKABLE | LV_BTNMATRIX_CTRL_RECOLOR);
|
|
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; // overwrites last newline
|
|
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);
|
|
|