mirror of
https://github.com/randybb/esphome-configs.git
synced 2026-10-14 16:35:12 +02:00
Compare commits
22
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8738b39e0 | ||
|
|
7ee975875a | ||
|
|
dd508aec99 | ||
|
|
e13be73596 | ||
|
|
b523963944 | ||
|
|
6ca813fb64 | ||
|
|
d80abdbb90 | ||
|
|
29b375df68 | ||
|
|
5db5987405 | ||
|
|
dec5e5de0a | ||
|
|
88e1214058 | ||
|
|
61032ad248 | ||
|
|
148c51c4df | ||
|
|
cc2ab4c467 | ||
|
|
504aa06d10 | ||
|
|
4b6544adfa | ||
|
|
6a51099789 | ||
|
|
369578a63a | ||
|
|
bdf49db1c2 | ||
|
|
349f699572 | ||
|
|
329830e553 | ||
|
|
d813717d2c |
No files matched your search
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1194" height="240" fill="none" viewBox="0 0 1194 240"><g clip-path="url(#clip0_2_63)"><path fill="#18BCF2" d="M240 219C240 227.24 233.24 234 225 234H15C6.76 234 0 227.24 0 219V129C0 120.76 4.78 109.22 10.6 103.4L109.4 4.61999C115.24 -1.22001 124.78 -1.22001 130.62 4.61999L229.4 103.4C235.24 109.24 240 120.76 240 129V219Z"/><path fill="#F2F4F9" d="M160 78.66H80C76.68 78.66 74 81.34 74 84.66V234H86V90.66H154V102.66H104C100.68 102.66 98 105.34 98 108.66V132.66C98 135.98 100.68 138.66 104 138.66H154V150.66H104C100.68 150.66 98 153.34 98 156.66V180.66C98 183.98 100.68 186.66 104 186.66H154V198.66H104C100.68 198.66 98 201.34 98 204.66C98 207.98 100.68 210.66 104 210.66H160C163.32 210.66 166 207.98 166 204.66V180.66C166 177.34 163.32 174.66 160 174.66H110V162.66H160C163.32 162.66 166 159.98 166 156.66V132.66C166 129.34 163.32 126.66 160 126.66H110V114.66H160C163.32 114.66 166 111.98 166 108.66V84.66C166 81.34 163.32 78.66 160 78.66Z"/><path fill="#F2F4F9" d="M423 167.61V190.11H336V49.95H423V72.63H359.82V108.91H416.26V130.01H359.82V167.61H423Z"/><path fill="#F2F4F9" d="M491.08 47.41C503.9 47.41 514.54 50.53 523.04 56.79C531.54 63.03 536.76 71.39 538.7 81.83L516.3 87.93C515.04 81.93 512.12 77.31 507.54 74.05C502.94 70.81 497.28 69.17 490.52 69.17C483.26 69.17 477.52 70.91 473.28 74.37C469.02 77.83 466.9 82.45 466.9 88.19C466.9 97.25 472.52 103.13 483.78 105.81L506.66 111.53C518.48 114.59 527.32 119.45 533.2 126.11C539.08 132.77 542.02 140.97 542.02 150.71C542.02 163.21 537.48 173.31 528.42 181.03C519.36 188.75 507.24 192.61 492.04 192.61C478.36 192.61 466.78 189.39 457.36 182.95C448.04 176.27 442.54 167.45 440.86 156.51L463.26 150.69C464.08 156.87 467.16 161.73 472.5 165.23C477.84 168.73 484.64 170.47 492.9 170.47C501.16 170.47 507.08 168.79 511.6 165.45C516.14 162.11 518.4 157.57 518.4 151.81C518.4 142.81 512.78 136.87 501.52 133.99L478.64 128.45C466.88 125.77 458.06 120.99 452.16 114.15C446.26 107.31 443.3 98.95 443.3 89.07C443.3 76.69 447.7 66.65 456.48 58.93C465.26 51.21 476.8 47.35 491.12 47.35L491.08 47.41Z"/><path fill="#F2F4F9" d="M666.1 95.61C666.1 109.37 661.92 120.49 653.58 128.99C645.24 137.49 634 141.75 619.88 141.75H588.38V190.13H564.56V49.95H620.06C634.3 49.95 645.54 54.07 653.76 62.27C661.98 70.49 666.08 81.59 666.08 95.59L666.1 95.61ZM641.16 95.05C641.16 87.99 638.96 82.25 634.56 77.85C630.16 73.45 623.92 71.25 615.86 71.25H588.4V120.85H615.5C623.74 120.85 630.1 118.51 634.54 113.81C638.98 109.13 641.2 102.87 641.2 95.05H641.16Z"/><path fill="#F2F4F9" d="M687.38 49.95H711.2V108.55L772.98 108.73V49.95H796.98V190.11H772.98V129.83L711.2 129.65V190.11H687.38V49.95Z"/><path fill="#F2F4F9" d="M868.6 92.79C883.1 92.79 894.92 97.45 904.04 106.75C913.16 116.07 917.72 128.03 917.72 142.65C917.72 157.27 913.16 169.15 904.04 178.47C894.92 187.79 883.1 192.43 868.6 192.43C854.1 192.43 842.1 187.77 832.98 178.47C823.86 169.15 819.3 157.23 819.3 142.65C819.3 128.07 823.86 115.97 832.98 106.69C842.1 97.41 853.98 92.77 868.6 92.77V92.79ZM868.6 172.01C876.34 172.01 882.64 169.27 887.5 163.81C892.34 158.35 894.76 151.23 894.76 142.49C894.76 133.75 892.34 126.67 887.5 121.25C882.66 115.85 876.36 113.15 868.6 113.15C860.84 113.15 854.26 115.85 849.38 121.25C844.5 126.65 842.06 133.73 842.06 142.49C842.06 151.25 844.5 158.35 849.38 163.81C854.26 169.27 860.66 172.01 868.6 172.01Z"/><path fill="#F2F4F9" d="M1081.6 129.83V190.11H1059.56V133.29C1059.56 126.73 1057.88 121.61 1054.5 117.91C1051.12 114.23 1046.54 112.37 1040.72 112.37C1034.54 112.37 1029.6 114.39 1025.9 118.41C1022.22 122.45 1020.36 128.09 1020.36 135.33V190.09H998.04V133.27C998.04 126.71 996.4 121.59 993.12 117.89C989.84 114.21 985.3 112.35 979.48 112.35C973.3 112.35 968.32 114.37 964.58 118.39C960.84 122.41 958.96 128.07 958.96 135.31V190.07H936.28V95.39H957.38L958.14 104.49C963.64 96.43 972.6 92.39 985.04 92.39C992.28 92.39 998.58 93.89 1003.88 96.89C1009.2 99.89 1013.22 104.27 1015.98 110.01C1018.3 104.51 1022.12 100.19 1027.46 97.07C1032.8 93.95 1039.16 92.39 1046.54 92.39C1057.28 92.39 1065.82 95.71 1072.14 102.33C1078.46 108.95 1081.6 118.11 1081.6 129.79V129.83Z"/><path fill="#F2F4F9" d="M1193.54 151.11H1120.98C1121.74 158.23 1124.42 163.67 1129.04 167.43C1133.66 171.19 1139.54 173.05 1146.66 173.05C1158.22 173.05 1166.32 168.23 1170.94 158.61L1189.88 166.11C1186.32 174.43 1180.7 180.93 1173.06 185.61C1165.4 190.29 1156.6 192.65 1146.66 192.65C1132.66 192.65 1121.3 188.07 1112.58 178.91C1103.86 169.75 1099.5 157.73 1099.5 142.87C1099.5 128.01 1103.9 115.91 1112.68 106.59C1121.46 97.27 1132.94 92.63 1147.14 92.63C1161.34 92.63 1172.3 97.23 1180.8 106.41C1189.3 115.59 1193.56 127.69 1193.56 142.69V151.13L1193.54 151.11ZM1121.36 135.17H1170.48C1170.04 127.49 1167.72 121.63 1163.5 117.59C1159.28 113.55 1153.6 111.55 1146.48 111.55C1139.36 111.55 1133.66 113.59 1129.24 117.69C1124.8 121.79 1122.18 127.61 1121.36 135.17Z"/></g><defs><clipPath id="clip0_2_63"><rect width="1193.56" height="240" fill="#fff"/></cliLine truncated
|
|
||||||
|
Before Width: | Height: | Size: 4.9 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_2" data-name="Layer 2" viewBox="0 0 681.98 201.98"><defs><style>.cls-1{fill:none;stroke:#1d2126;stroke-miterlimit:10;stroke-width:1.01px}.cls-2{fill:#1d2126}.cls-2,.cls-3,.cls-4{stroke-width:0}.cls-3{fill:#f2f4f9}.cls-4{fill:#18bcf2}</style></defs><g id="Layer_1-2" data-name="Layer 1"><g><g><rect width="680.97" height="200.97" x=".51" y=".51" class="cls-3" rx="40.48" ry="40.48"/><rect width="680.97" height="200.97" x=".51" y=".51" class="cls-1" rx="40.48" ry="40.48"/></g><g><path d="M236.13,43.11v26.53h-5.29v-14.5l-5.68,14.5h-5.08l-5.61-14.4v14.4h-5.12v-26.53h5.12l8.16,20.27,8.21-20.27h5.29Z" class="cls-4"/><path d="M259.54,63.84h-9.04l-2.02,5.8h-5.59l9.51-26.53h5.33l9.47,26.53h-5.7l-1.97-5.8ZM258,59.34l-2.95-8.7-2.99,8.7h5.94Z" class="cls-4"/><path d="M296.93,56.42c.02,1.89-.3,3.66-.98,5.31s-1.61,3.06-2.8,4.22-2.61,2.07-4.26,2.73-3.4.98-5.26.96h-9.65v-26.53h9.65c2.43-.04,4.66.52,6.71,1.68s3.67,2.76,4.86,4.82,1.78,4.33,1.74,6.81ZM291.25,56.42c0-2.46-.71-4.46-2.12-5.99s-3.27-2.29-5.58-2.29h-4.22v16.52h4.22c2.34,0,4.21-.75,5.61-2.26s2.09-3.5,2.09-5.99Z" class="cls-4"/><path d="M321.38,64.59v5.04h-16.7v-26.53h16.7v5.1h-11.34v5.66h10.07v4.75h-10.07v5.98h11.34Z" class="cls-4"/><path d="M348.35,48.17v6.1h9.58l-.04,4.82h-9.54v10.55h-5.36v-26.53h16.73l-.04,5.06h-11.34Z" class="cls-4"/><path d="M378.62,42.64c1.9,0,3.66.34,5.27,1.03s2.99,1.63,4.11,2.84,2,2.66,2.63,4.35.94,3.52.94,5.49c.02,1.95-.28,3.77-.91,5.48s-1.52,3.17-2.65,4.39-2.52,2.17-4.15,2.86-3.39,1.03-5.27,1c-2.5.04-4.74-.54-6.72-1.72s-3.52-2.82-4.6-4.92-1.6-4.45-1.56-7.05c-.02-1.95.28-3.77.91-5.48s1.51-3.18,2.65-4.39,2.51-2.17,4.13-2.87,3.37-1.03,5.23-1ZM378.65,64.82c2.24,0,4.01-.76,5.32-2.28s1.96-3.58,1.96-6.2-.65-4.71-1.95-6.22-3.08-2.27-5.33-2.27-4.03.76-5.33,2.27-1.95,3.59-1.95,6.22.65,4.71,1.95,6.21,3.08,2.26,5.33,2.26Z" class="cls-4"/><path d="M419.5,69.64h-6.01l-4.08-9.58h-4.89v9.58h-5.36v-26.53h10.21c3.28.01,5.76.74,7.44,2.19s2.51,3.55,2.51,6.3c0,1.79-.39,3.34-1.17,4.65s-1.89,2.28-3.33,2.91l4.68,10.48ZM413.63,51.6c0-1.15-.36-2.05-1.07-2.72s-1.78-.99-3.18-.99h-4.85v7.49h4.85c1.41,0,2.47-.34,3.18-1.01s1.07-1.6,1.07-2.77Z" class="cls-4"/></g><g><path d="M251.92,146.83v11.25h-43.5v-70.08h43.5v11.34h-31.59v18.14h28.22v10.55h-28.22v18.8h31.59Z" class="cls-2"/><path d="M285,86.74c6.41,0,11.73,1.56,15.98,4.69,4.25,3.12,6.86,7.3,7.83,12.52l-11.2,3.05c-.62-3-2.09-5.31-4.38-6.94-2.3-1.62-5.13-2.44-8.51-2.44-3.62,0-6.5.87-8.62,2.6s-3.19,4.04-3.19,6.91c0,4.53,2.81,7.47,8.44,8.81l11.44,2.86c5.91,1.53,10.33,3.96,13.27,7.29s4.41,7.43,4.41,12.3c0,6.25-2.27,11.3-6.8,15.16-4.53,3.86-10.59,5.79-18.19,5.79-6.84,0-12.62-1.61-17.34-4.83-4.66-3.34-7.41-7.75-8.25-13.22l11.2-2.91c.41,3.09,1.95,5.52,4.62,7.27,2.67,1.75,6.07,2.62,10.2,2.62s7.09-.84,9.35-2.51c2.27-1.67,3.4-3.94,3.4-6.82,0-4.5-2.81-7.47-8.44-8.91l-11.44-2.77c-5.88-1.34-10.29-3.73-13.24-7.15s-4.43-7.6-4.43-12.54c0-6.19,2.2-11.21,6.59-15.07,4.39-3.86,10.16-5.79,17.32-5.79Z" class="cls-2"/><path d="M371.55,110.83c0,6.88-2.09,12.44-6.26,16.69-4.17,4.25-9.79,6.38-16.85,6.38h-15.75v24.19h-11.91v-70.08h27.75c7.12,0,12.74,2.06,16.85,6.16,4.11,4.11,6.16,9.66,6.16,16.66ZM359.08,110.55c0-3.53-1.1-6.4-3.3-8.6s-5.32-3.3-9.35-3.3h-13.73v24.8h13.55c4.12,0,7.3-1.17,9.52-3.52s3.33-5.47,3.33-9.38Z" class="cls-2"/><path d="M381.23,88h11.91v29.3l30.89.09v-29.39h12v70.08h-12v-30.14l-30.89-.09v30.23h-11.91v-70.08Z" class="cls-2"/><path d="M470.88,109.43c7.25,0,13.16,2.33,17.72,6.98,4.56,4.66,6.84,10.64,6.84,17.95s-2.28,13.25-6.84,17.91c-4.56,4.66-10.47,6.98-17.72,6.98s-13.25-2.33-17.81-6.98c-4.56-4.66-6.84-10.62-6.84-17.91s2.28-13.34,6.84-17.98,10.5-6.96,17.81-6.96ZM470.88,149.04c3.88,0,7.02-1.37,9.45-4.1,2.42-2.73,3.63-6.29,3.63-10.66s-1.21-7.91-3.63-10.62c-2.42-2.7-5.57-4.05-9.45-4.05s-7.17,1.35-9.61,4.05c-2.44,2.7-3.66,6.24-3.66,10.62s1.22,7.93,3.66,10.66c2.44,2.73,5.64,4.1,9.61,4.1Z" class="cls-2"/><path d="M576.42,127.94v30.14h-11.02v-28.41c0-3.28-.84-5.84-2.53-7.69-1.69-1.84-3.98-2.77-6.89-2.77-3.09,0-5.56,1.01-7.41,3.02s-2.77,4.84-2.77,8.46v27.38h-11.16v-28.41c0-3.28-.82-5.84-2.46-7.69-1.64-1.84-3.91-2.77-6.82-2.77-3.09,0-5.58,1.01-7.45,3.02s-2.81,4.84-2.81,8.46v27.38h-11.34v-47.34h10.55l.38,4.55c2.75-4.03,7.23-6.05,13.45-6.05,3.62,0,6.77.75,9.42,2.25s4.67,3.69,6.05,6.56c1.16-2.75,3.07-4.91,5.74-6.47,2.67-1.56,5.85-2.34,9.54-2.34,5.38,0,9.64,1.66,12.8,4.97,3.16,3.31,4.73,7.89,4.73,13.73Z" class="cls-2"/><path d="M631.43,138.58h-36.28c.38,3.56,1.72,6.28,4.03,8.16s5.25,2.81,8.81,2.81c5.78,0,9.83-2.41,12.14-7.22l9.47,3.75c-1.78,4.16-4.59,7.41-8.41,9.75s-8.23,3.52-13.2,3.52c-7,0-12.68-2.29-17.04-6.87-4.36-4.58-6.54-10.59-6.54-18.02s2.2-13.48,6.59-18.14c4.39-4.66,10.13-6.98,17.23-6.98s12.58,2.3,16.83,6.89,6.38,10.64,6.38,18.14v4.22ZM595.34,130.61h24.56c-.22-3.84-1.38-6.77-3.49-8.79s-4.95-3.02-8.51-3.02-6.41,1.02-8.62,3.07c-2.22,2.05-3.53,4.96-3.94,8.74Z" class="cls-2"/></g><g><path d="M170.55,153.49c0,4.12-3.38,7.5-7.5,7.5H58.05c-4.12,0-7.5-3.38-7.5-7.5v-45c0-4.12,2.39-9.89,5.3-12Line truncated
|
||||||
|
After Width: | Height: | Size: 5.3 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_2" data-name="Layer 2" viewBox="0 0 681.98 201.98"><defs><style>.cls-1{fill:#1d2126}.cls-1,.cls-2,.cls-3{stroke-width:0}.cls-2{fill:#f2f4f9}.cls-3{fill:#18bcf2}.cls-4{fill:none;stroke:#f2f4f9;stroke-miterlimit:10;stroke-width:1.01px}</style></defs><g id="Layer_1-2" data-name="Layer 1"><g><g><rect width="680.97" height="200.97" x=".51" y=".51" class="cls-1" rx="40.48" ry="40.48"/><rect width="680.97" height="200.97" x=".51" y=".51" class="cls-4" rx="40.48" ry="40.48"/></g><g><path d="M236.13,43.11v26.53h-5.29v-14.5l-5.68,14.5h-5.08l-5.61-14.4v14.4h-5.12v-26.53h5.12l8.16,20.27,8.21-20.27h5.29Z" class="cls-3"/><path d="M259.54,63.84h-9.04l-2.02,5.8h-5.59l9.51-26.53h5.33l9.47,26.53h-5.7l-1.97-5.8ZM258,59.34l-2.95-8.7-2.99,8.7h5.94Z" class="cls-3"/><path d="M296.93,56.42c.02,1.89-.3,3.66-.98,5.31s-1.61,3.06-2.8,4.22-2.61,2.07-4.26,2.73-3.4.98-5.26.96h-9.65v-26.53h9.65c2.43-.04,4.66.52,6.71,1.68s3.67,2.76,4.86,4.82,1.78,4.33,1.74,6.81ZM291.25,56.42c0-2.46-.71-4.46-2.12-5.99s-3.27-2.29-5.58-2.29h-4.22v16.52h4.22c2.34,0,4.21-.75,5.61-2.26s2.09-3.5,2.09-5.99Z" class="cls-3"/><path d="M321.38,64.59v5.04h-16.7v-26.53h16.7v5.1h-11.34v5.66h10.07v4.75h-10.07v5.98h11.34Z" class="cls-3"/><path d="M348.35,48.17v6.1h9.58l-.04,4.82h-9.54v10.55h-5.36v-26.53h16.73l-.04,5.06h-11.34Z" class="cls-3"/><path d="M378.62,42.64c1.9,0,3.66.34,5.27,1.03s2.99,1.63,4.11,2.84,2,2.66,2.63,4.35.94,3.52.94,5.49c.02,1.95-.28,3.77-.91,5.48s-1.52,3.17-2.65,4.39-2.52,2.17-4.15,2.86-3.39,1.03-5.27,1c-2.5.04-4.74-.54-6.72-1.72s-3.52-2.82-4.6-4.92-1.6-4.45-1.56-7.05c-.02-1.95.28-3.77.91-5.48s1.51-3.18,2.65-4.39,2.51-2.17,4.13-2.87,3.37-1.03,5.23-1ZM378.65,64.82c2.24,0,4.01-.76,5.32-2.28s1.96-3.58,1.96-6.2-.65-4.71-1.95-6.22-3.08-2.27-5.33-2.27-4.03.76-5.33,2.27-1.95,3.59-1.95,6.22.65,4.71,1.95,6.21,3.08,2.26,5.33,2.26Z" class="cls-3"/><path d="M419.5,69.64h-6.01l-4.08-9.58h-4.89v9.58h-5.36v-26.53h10.21c3.28.01,5.76.74,7.44,2.19s2.51,3.55,2.51,6.3c0,1.79-.39,3.34-1.17,4.65s-1.89,2.28-3.33,2.91l4.68,10.48ZM413.63,51.6c0-1.15-.36-2.05-1.07-2.72s-1.78-.99-3.18-.99h-4.85v7.49h4.85c1.41,0,2.47-.34,3.18-1.01s1.07-1.6,1.07-2.77Z" class="cls-3"/></g><g><path d="M251.92,146.83v11.25h-43.5v-70.08h43.5v11.34h-31.59v18.14h28.22v10.55h-28.22v18.8h31.59Z" class="cls-2"/><path d="M285,86.74c6.41,0,11.73,1.56,15.98,4.69,4.25,3.12,6.86,7.3,7.83,12.52l-11.2,3.05c-.62-3-2.09-5.31-4.38-6.94-2.3-1.62-5.13-2.44-8.51-2.44-3.62,0-6.5.87-8.62,2.6s-3.19,4.04-3.19,6.91c0,4.53,2.81,7.47,8.44,8.81l11.44,2.86c5.91,1.53,10.33,3.96,13.27,7.29s4.41,7.43,4.41,12.3c0,6.25-2.27,11.3-6.8,15.16-4.53,3.86-10.59,5.79-18.19,5.79-6.84,0-12.62-1.61-17.34-4.83-4.66-3.34-7.41-7.75-8.25-13.22l11.2-2.91c.41,3.09,1.95,5.52,4.62,7.27,2.67,1.75,6.07,2.62,10.2,2.62s7.09-.84,9.35-2.51c2.27-1.67,3.4-3.94,3.4-6.82,0-4.5-2.81-7.47-8.44-8.91l-11.44-2.77c-5.88-1.34-10.29-3.73-13.24-7.15s-4.43-7.6-4.43-12.54c0-6.19,2.2-11.21,6.59-15.07,4.39-3.86,10.16-5.79,17.32-5.79Z" class="cls-2"/><path d="M371.55,110.83c0,6.88-2.09,12.44-6.26,16.69-4.17,4.25-9.79,6.38-16.85,6.38h-15.75v24.19h-11.91v-70.08h27.75c7.12,0,12.74,2.06,16.85,6.16,4.11,4.11,6.16,9.66,6.16,16.66ZM359.08,110.55c0-3.53-1.1-6.4-3.3-8.6s-5.32-3.3-9.35-3.3h-13.73v24.8h13.55c4.12,0,7.3-1.17,9.52-3.52s3.33-5.47,3.33-9.38Z" class="cls-2"/><path d="M381.23,88h11.91v29.3l30.89.09v-29.39h12v70.08h-12v-30.14l-30.89-.09v30.23h-11.91v-70.08Z" class="cls-2"/><path d="M470.88,109.43c7.25,0,13.16,2.33,17.72,6.98,4.56,4.66,6.84,10.64,6.84,17.95s-2.28,13.25-6.84,17.91c-4.56,4.66-10.47,6.98-17.72,6.98s-13.25-2.33-17.81-6.98c-4.56-4.66-6.84-10.62-6.84-17.91s2.28-13.34,6.84-17.98,10.5-6.96,17.81-6.96ZM470.88,149.04c3.88,0,7.02-1.37,9.45-4.1,2.42-2.73,3.63-6.29,3.63-10.66s-1.21-7.91-3.63-10.62c-2.42-2.7-5.57-4.05-9.45-4.05s-7.17,1.35-9.61,4.05c-2.44,2.7-3.66,6.24-3.66,10.62s1.22,7.93,3.66,10.66c2.44,2.73,5.64,4.1,9.61,4.1Z" class="cls-2"/><path d="M576.42,127.94v30.14h-11.02v-28.41c0-3.28-.84-5.84-2.53-7.69-1.69-1.84-3.98-2.77-6.89-2.77-3.09,0-5.56,1.01-7.41,3.02s-2.77,4.84-2.77,8.46v27.38h-11.16v-28.41c0-3.28-.82-5.84-2.46-7.69-1.64-1.84-3.91-2.77-6.82-2.77-3.09,0-5.58,1.01-7.45,3.02s-2.81,4.84-2.81,8.46v27.38h-11.34v-47.34h10.55l.38,4.55c2.75-4.03,7.23-6.05,13.45-6.05,3.62,0,6.77.75,9.42,2.25s4.67,3.69,6.05,6.56c1.16-2.75,3.07-4.91,5.74-6.47,2.67-1.56,5.85-2.34,9.54-2.34,5.38,0,9.64,1.66,12.8,4.97,3.16,3.31,4.73,7.89,4.73,13.73Z" class="cls-2"/><path d="M631.43,138.58h-36.28c.38,3.56,1.72,6.28,4.03,8.16s5.25,2.81,8.81,2.81c5.78,0,9.83-2.41,12.14-7.22l9.47,3.75c-1.78,4.16-4.59,7.41-8.41,9.75s-8.23,3.52-13.2,3.52c-7,0-12.68-2.29-17.04-6.87-4.36-4.58-6.54-10.59-6.54-18.02s2.2-13.48,6.59-18.14c4.39-4.66,10.13-6.98,17.23-6.98s12.58,2.3,16.83,6.89,6.38,10.64,6.38,18.14v4.22ZM595.34,130.61h24.56c-.22-3.84-1.38-6.77-3.49-8.79s-4.95-3.02-8.51-3.02-6.41,1.02-8.62,3.07c-2.22,2.05-3.53,4.96-3.94,8.74Z" class="cls-2"/></g><g><path d="M170.55,153.49c0,4.12-3.38,7.5-7.5,7.5H58.05c-4.12,0-7.5-3.38-7.5-7.5v-45c0-4.12,2.39-9.89,5.3-12Line truncated
|
||||||
|
After Width: | Height: | Size: 5.3 KiB |
+2
-2
@@ -24,8 +24,8 @@ text_sensor:
|
|||||||
# - <<: !include text_sensor/uptime.yaml
|
# - <<: !include text_sensor/uptime.yaml
|
||||||
- <<: !include text_sensor/wifi_info.yaml
|
- <<: !include text_sensor/wifi_info.yaml
|
||||||
|
|
||||||
# time:
|
time:
|
||||||
#. - <<: !include time/homeassistant.yaml
|
- <<: !include time/homeassistant.yaml
|
||||||
|
|
||||||
network:
|
network:
|
||||||
enable_ipv6: true
|
enable_ipv6: true
|
||||||
@@ -24,5 +24,8 @@ text_sensor:
|
|||||||
- <<: !include text_sensor/version.yaml
|
- <<: !include text_sensor/version.yaml
|
||||||
- <<: !include text_sensor/wifi_info.yaml
|
- <<: !include text_sensor/wifi_info.yaml
|
||||||
|
|
||||||
|
time:
|
||||||
|
- <<: !include time/homeassistant.yaml
|
||||||
|
|
||||||
network:
|
network:
|
||||||
enable_ipv6: true
|
enable_ipv6: true
|
||||||
@@ -23,5 +23,8 @@ text_sensor:
|
|||||||
- <<: !include text_sensor/version.yaml
|
- <<: !include text_sensor/version.yaml
|
||||||
- <<: !include text_sensor/wifi_info.yaml
|
- <<: !include text_sensor/wifi_info.yaml
|
||||||
|
|
||||||
|
time:
|
||||||
|
- <<: !include time/homeassistant.yaml
|
||||||
|
|
||||||
network:
|
network:
|
||||||
enable_ipv6: true
|
enable_ipv6: true
|
||||||
@@ -21,5 +21,8 @@ text_sensor:
|
|||||||
- <<: !include text_sensor/version.yaml
|
- <<: !include text_sensor/version.yaml
|
||||||
- <<: !include text_sensor/ethernet_info.yaml
|
- <<: !include text_sensor/ethernet_info.yaml
|
||||||
|
|
||||||
|
time:
|
||||||
|
- <<: !include time/homeassistant.yaml
|
||||||
|
|
||||||
network:
|
network:
|
||||||
enable_ipv6: true
|
enable_ipv6: true
|
||||||
@@ -27,3 +27,6 @@ text_sensor:
|
|||||||
- <<: !include text_sensor/git_ref.yaml
|
- <<: !include text_sensor/git_ref.yaml
|
||||||
- <<: !include text_sensor/version.yaml
|
- <<: !include text_sensor/version.yaml
|
||||||
- <<: !include text_sensor/wifi_info.yaml
|
- <<: !include text_sensor/wifi_info.yaml
|
||||||
|
|
||||||
|
time:
|
||||||
|
- <<: !include time/homeassistant.yaml
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
# IPv6-only IoT network (SLAAC, no DHCPv4); add next to a common package:
|
||||||
|
# packages:
|
||||||
|
# common: !include common/common.yaml
|
||||||
|
# ipv6_only: !include common/ipv6_only.yaml
|
||||||
|
external_components:
|
||||||
|
# network enable_ipv4 (esphome/esphome#19750), drop once merged
|
||||||
|
- source: github://pr#19750
|
||||||
|
components: [const, network, wifi]
|
||||||
|
# audio HTTP reader over IPv6 (dual-stack hosts resolve A first), drop once in #19750
|
||||||
|
- source: github://randybb/esphome-components
|
||||||
|
components: [audio]
|
||||||
|
refresh: 1h
|
||||||
|
|
||||||
|
wifi:
|
||||||
|
networks:
|
||||||
|
- id: !extend wifi_iot
|
||||||
|
ssid: !secret wifi_ssid_ipv6
|
||||||
|
password: !secret wifi_password_ipv6
|
||||||
|
|
||||||
|
esp32:
|
||||||
|
framework:
|
||||||
|
components:
|
||||||
|
# mdns 1.12.0 + fix for "A 0.0.0.0" without IPv4 (espressif/esp-protocols PR pending), drop once released
|
||||||
|
- name: espressif/mdns
|
||||||
|
source: https://github.com/randybb/esp-protocols.git
|
||||||
|
ref: mdns-v1.12.0-zero-ipv4-test
|
||||||
|
path: components/mdns
|
||||||
|
# IDF v5.5.5 lwip + .local resolution over IPv6 (espressif/esp-lwip#94), drop once in IDF
|
||||||
|
- name: lwip
|
||||||
|
source: https://github.com/randybb/esp-lwip.git
|
||||||
|
ref: idf-v5.5.5-lwip-component
|
||||||
|
path: lwip
|
||||||
|
|
||||||
|
network:
|
||||||
|
enable_ipv6: true
|
||||||
|
enable_ipv4: false
|
||||||
|
min_ipv6_addr_count: 1
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
---
|
||||||
|
# Sonoff NSPanel with the native Lovelace UI (randybb/esphome-nspanel-lovelace-native).
|
||||||
|
# The device file adds the cards, buttons and relays:
|
||||||
|
# packages:
|
||||||
|
# common: !include common/common.yaml
|
||||||
|
# nspanel: !include
|
||||||
|
# file: common/nspanel.yaml
|
||||||
|
# vars:
|
||||||
|
# temperature_entity: sensor.zigbee_bedroom_temperature
|
||||||
|
# nspanel_lovelace:
|
||||||
|
# cards: [...]
|
||||||
|
defaults:
|
||||||
|
temperature_name: Temperature
|
||||||
|
ntc_reference_temperature: 25°C
|
||||||
|
|
||||||
|
esphome:
|
||||||
|
on_boot:
|
||||||
|
then:
|
||||||
|
- switch.turn_off: screen_power
|
||||||
|
- delay: 1s
|
||||||
|
- switch.turn_on: screen_power
|
||||||
|
- delay: 2s
|
||||||
|
- lambda: 'id(nspanel_id).set_display_dim(5, 80);'
|
||||||
|
|
||||||
|
esp32:
|
||||||
|
variant: esp32
|
||||||
|
toolchain: esp-idf
|
||||||
|
framework:
|
||||||
|
type: esp-idf
|
||||||
|
advanced:
|
||||||
|
minimum_chip_revision: 3.0
|
||||||
|
sram1_as_iram: true
|
||||||
|
|
||||||
|
external_components:
|
||||||
|
- source:
|
||||||
|
type: git
|
||||||
|
url: https://github.com/randybb/esphome-nspanel-lovelace-native
|
||||||
|
ref: devices
|
||||||
|
refresh: 0s
|
||||||
|
components: [nspanel_lovelace]
|
||||||
|
|
||||||
|
api:
|
||||||
|
homeassistant_services: true
|
||||||
|
homeassistant_states: true
|
||||||
|
actions:
|
||||||
|
- action: play_rtttl
|
||||||
|
variables:
|
||||||
|
song: string
|
||||||
|
then:
|
||||||
|
- rtttl.play:
|
||||||
|
rtttl: !lambda 'return song;'
|
||||||
|
|
||||||
|
- action: notify
|
||||||
|
variables:
|
||||||
|
heading: string
|
||||||
|
message: string
|
||||||
|
song: string
|
||||||
|
then:
|
||||||
|
- lambda: 'id(nspanel_id).notify_on_screensaver(heading, message, 10000);'
|
||||||
|
- rtttl.play:
|
||||||
|
rtttl: !lambda 'return song;'
|
||||||
|
|
||||||
|
- action: notify_fullscreen
|
||||||
|
variables:
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
button1: string
|
||||||
|
button2: string
|
||||||
|
timeout: int
|
||||||
|
icon: string
|
||||||
|
then:
|
||||||
|
- lambda: 'id(nspanel_id).notify_on_screensaver(title, description, timeout);'
|
||||||
|
- rtttl.play: "scale_up:d=32,o=5,b=100:c,c#,d#,e,f#,g#,a#,b"
|
||||||
|
|
||||||
|
uart:
|
||||||
|
id: display_uart
|
||||||
|
tx_pin: 16
|
||||||
|
rx_pin: 17
|
||||||
|
baud_rate: 115200
|
||||||
|
|
||||||
|
nspanel_lovelace:
|
||||||
|
id: nspanel_id
|
||||||
|
sleep_timeout: 10
|
||||||
|
experimental_vertical_ui: true
|
||||||
|
locale:
|
||||||
|
language: sk
|
||||||
|
temperature_unit: celcius
|
||||||
|
screensaver:
|
||||||
|
time_id: ha_time
|
||||||
|
time_format: '%H:%M'
|
||||||
|
date_format: '%A, %d. %b. %Y'
|
||||||
|
weather:
|
||||||
|
entity_id: sensor.weather_forecast_daily
|
||||||
|
icon_color: [255, 255, 255]
|
||||||
|
entities:
|
||||||
|
- entity_id: ${temperature_entity}
|
||||||
|
icon:
|
||||||
|
value: home-thermometer-outline
|
||||||
|
color: [255, 255, 255]
|
||||||
|
precision: 1
|
||||||
|
|
||||||
|
output:
|
||||||
|
- platform: ledc
|
||||||
|
id: buzzer_out
|
||||||
|
pin:
|
||||||
|
number: 21
|
||||||
|
|
||||||
|
switch:
|
||||||
|
- platform: gpio
|
||||||
|
name: Screen Power
|
||||||
|
id: screen_power
|
||||||
|
entity_category: config
|
||||||
|
pin:
|
||||||
|
number: 4
|
||||||
|
inverted: true
|
||||||
|
restore_mode: ALWAYS_ON
|
||||||
|
|
||||||
|
rtttl:
|
||||||
|
id: buzzer
|
||||||
|
output: buzzer_out
|
||||||
|
|
||||||
|
sensor:
|
||||||
|
- platform: adc
|
||||||
|
id: ntc_source
|
||||||
|
pin: 38
|
||||||
|
update_interval: 5min
|
||||||
|
attenuation: 12db
|
||||||
|
|
||||||
|
- platform: resistance
|
||||||
|
id: resistance_sensor
|
||||||
|
sensor: ntc_source
|
||||||
|
configuration: DOWNSTREAM
|
||||||
|
resistor: 11.2kOhm
|
||||||
|
|
||||||
|
- platform: ntc
|
||||||
|
name: ${temperature_name}
|
||||||
|
id: temperature
|
||||||
|
sensor: resistance_sensor
|
||||||
|
calibration:
|
||||||
|
b_constant: 3950
|
||||||
|
reference_temperature: ${ntc_reference_temperature}
|
||||||
|
reference_resistance: 10kOhm
|
||||||
|
|
||||||
|
text_sensor:
|
||||||
|
- platform: homeassistant
|
||||||
|
id: name_day
|
||||||
|
entity_id: sensor.nameday_sk
|
||||||
|
on_value:
|
||||||
|
- lambda: |-
|
||||||
|
id(nspanel_id).set_time_format("%H:%M~" + x);
|
||||||
|
id(nspanel_id).update_time();
|
||||||
|
|
||||||
|
# HMI protocol version reported by the TFT on startup (52/53 for this UI)
|
||||||
|
- platform: template
|
||||||
|
name: TFT Version
|
||||||
|
entity_category: diagnostic
|
||||||
|
update_interval: 60s
|
||||||
|
lambda: |-
|
||||||
|
using esphome::nspanel_lovelace::Configuration;
|
||||||
|
const auto ver = Configuration::get_version();
|
||||||
|
if (ver == 0) return {"unknown"};
|
||||||
|
return {std::to_string(ver) + " (" + Configuration::get_model_str() + ")"};
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
# NSPanel screensaver dims to 0 at night (sun below -6°), unless the room lights are on.
|
||||||
|
# packages:
|
||||||
|
# sleep_dim: !include
|
||||||
|
# file: common/nspanel_sleep_dim.yaml
|
||||||
|
# vars:
|
||||||
|
# sleep_override_entity: light.bedroom_lights
|
||||||
|
script:
|
||||||
|
- id: apply_sleep_dim
|
||||||
|
then:
|
||||||
|
- lambda: |-
|
||||||
|
uint8_t dim;
|
||||||
|
if (id(sleep_override).state) dim = 5; // sleepOverride
|
||||||
|
else if (id(sun_id).is_above_horizon(-6)) dim = 5; // day
|
||||||
|
else dim = 0; // night
|
||||||
|
id(nspanel_id).set_display_inactive_dim(dim);
|
||||||
|
|
||||||
|
time:
|
||||||
|
- id: !extend ha_time
|
||||||
|
on_time_sync:
|
||||||
|
- script.execute: apply_sleep_dim
|
||||||
|
|
||||||
|
binary_sensor:
|
||||||
|
- platform: homeassistant # sleepOverride
|
||||||
|
id: sleep_override
|
||||||
|
entity_id: ${sleep_override_entity}
|
||||||
|
on_state:
|
||||||
|
- script.execute: apply_sleep_dim
|
||||||
|
|
||||||
|
sun:
|
||||||
|
id: sun_id
|
||||||
|
time_id: ha_time
|
||||||
|
latitude: 48.15°
|
||||||
|
longitude: 17.11°
|
||||||
|
on_sunrise:
|
||||||
|
- elevation: -6°
|
||||||
|
then:
|
||||||
|
- script.execute: apply_sleep_dim
|
||||||
|
on_sunset:
|
||||||
|
- elevation: -6°
|
||||||
|
then:
|
||||||
|
- script.execute: apply_sleep_dim
|
||||||
+3
-1
@@ -2,7 +2,9 @@
|
|||||||
wifi:
|
wifi:
|
||||||
# reboot_timeout: 0s
|
# reboot_timeout: 0s
|
||||||
networks:
|
networks:
|
||||||
- ssid: !secret wifi_ssid
|
# id lets devices override the network with '!extend wifi_iot'
|
||||||
|
- id: wifi_iot
|
||||||
|
ssid: !secret wifi_ssid
|
||||||
password: !secret wifi_password
|
password: !secret wifi_password
|
||||||
enable_btm: true
|
enable_btm: true
|
||||||
enable_rrm: true
|
enable_rrm: true
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
#pragma once
|
||||||
|
// Upcoming calendar events for mcu-m5paper: parsed from the calendar.get_events
|
||||||
|
// response and kept in flash (NVS), so they survive the power-off between RTC wake-ups.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "esphome/components/json/json_util.h"
|
||||||
|
#include "esphome/core/preferences.h"
|
||||||
|
#include "esphome/core/time.h"
|
||||||
|
|
||||||
|
namespace m5paper_calendar {
|
||||||
|
|
||||||
|
static constexpr uint8_t MAX_EVENTS = 32;
|
||||||
|
static constexpr size_t SUMMARY_LEN = 44;
|
||||||
|
static constexpr uint16_t ALL_DAY = 0xFFFF;
|
||||||
|
static constexpr uint32_t PREF_HASH = 0x6D35C41A;
|
||||||
|
|
||||||
|
enum Kind : uint8_t { BIRTHDAY = 0, WASTE };
|
||||||
|
|
||||||
|
struct Event {
|
||||||
|
uint16_t day; // local date, days since 1970-01-01
|
||||||
|
uint16_t minute; // local start time in minutes after midnight, ALL_DAY if none
|
||||||
|
uint8_t kind;
|
||||||
|
char summary[SUMMARY_LEN + 1];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Events {
|
||||||
|
uint32_t fetched_at{0}; // UTC epoch of the download
|
||||||
|
uint8_t count{0};
|
||||||
|
Event items[MAX_EVENTS]{};
|
||||||
|
|
||||||
|
bool valid() const { return this->fetched_at != 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// the events in RAM, loaded from flash at boot
|
||||||
|
inline Events &events() {
|
||||||
|
static Events ev;
|
||||||
|
return ev;
|
||||||
|
}
|
||||||
|
|
||||||
|
// days since 1970-01-01 for a proleptic Gregorian date (H. Hinnant's algorithm)
|
||||||
|
inline int32_t days_from_civil(int y, int m, int d) {
|
||||||
|
y -= m <= 2;
|
||||||
|
const int era = (y >= 0 ? y : y - 399) / 400;
|
||||||
|
const int yoe = y - era * 400;
|
||||||
|
const int doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1;
|
||||||
|
const int doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
|
||||||
|
return era * 146097 + doe - 719468;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int32_t local_day(const esphome::ESPTime &t) { return days_from_civil(t.year, t.month, t.day_of_month); }
|
||||||
|
|
||||||
|
// 0 = Monday
|
||||||
|
inline int weekday(int32_t day) { return (day + 3) % 7; }
|
||||||
|
|
||||||
|
// "2026-10-01" (all-day) or "2026-10-01T10:00:00+02:00" -> local day and minute; false on error
|
||||||
|
inline bool parse_start(const char *s, uint16_t &day, uint16_t &minute) {
|
||||||
|
int y, mo, d, h, mi, sec = 0, oh = 0, om = 0;
|
||||||
|
char sign = '+';
|
||||||
|
int n = sscanf(s, "%d-%d-%dT%d:%d:%d%c%d:%d", &y, &mo, &d, &h, &mi, &sec, &sign, &oh, &om);
|
||||||
|
if (n < 3)
|
||||||
|
return false;
|
||||||
|
if (n < 5) {
|
||||||
|
day = days_from_civil(y, mo, d);
|
||||||
|
minute = ALL_DAY;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
int64_t ts = int64_t(days_from_civil(y, mo, d)) * 86400 + h * 3600 + mi * 60 + sec;
|
||||||
|
if (n >= 8 && (sign == '+' || sign == '-')) {
|
||||||
|
int64_t offset = oh * 3600 + om * 60;
|
||||||
|
ts += sign == '+' ? -offset : offset;
|
||||||
|
}
|
||||||
|
if (ts <= 0)
|
||||||
|
return false;
|
||||||
|
const auto t = esphome::ESPTime::from_epoch_local(ts);
|
||||||
|
day = local_day(t);
|
||||||
|
minute = t.hour * 60 + t.minute;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy at most SUMMARY_LEN bytes without cutting a UTF-8 sequence
|
||||||
|
inline void copy_summary(char *dst, const char *src) {
|
||||||
|
size_t n = strnlen(src, SUMMARY_LEN + 1);
|
||||||
|
if (n > SUMMARY_LEN) {
|
||||||
|
n = SUMMARY_LEN;
|
||||||
|
while (n > 0 && (uint8_t(src[n]) & 0xC0) == 0x80)
|
||||||
|
n--;
|
||||||
|
}
|
||||||
|
memcpy(dst, src, n);
|
||||||
|
dst[n] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the calendar.get_events response; Home Assistant wraps the action response as
|
||||||
|
// {"response": {"calendar.x": {"events": [...]}, ...}}; every calendar other than the waste one holds birthdays
|
||||||
|
inline bool parse(ArduinoJson::JsonObjectConst response, const char *waste, uint32_t now, Events &ev) {
|
||||||
|
ArduinoJson::JsonObjectConst root = response["response"].is<ArduinoJson::JsonObjectConst>()
|
||||||
|
? response["response"].as<ArduinoJson::JsonObjectConst>()
|
||||||
|
: response;
|
||||||
|
if (root.size() == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Events out;
|
||||||
|
for (ArduinoJson::JsonPairConst cal : root) {
|
||||||
|
const uint8_t kind = strcmp(cal.key().c_str(), waste) == 0 ? WASTE : BIRTHDAY;
|
||||||
|
for (ArduinoJson::JsonObjectConst item : cal.value()["events"].as<ArduinoJson::JsonArrayConst>()) {
|
||||||
|
Event e;
|
||||||
|
if (!parse_start(item["start"] | "", e.day, e.minute))
|
||||||
|
continue;
|
||||||
|
e.kind = kind;
|
||||||
|
copy_summary(e.summary, item["summary"] | "");
|
||||||
|
// the same birthday in the contacts and the manual calendar
|
||||||
|
if (std::any_of(out.items, out.items + out.count,
|
||||||
|
[&e](const Event &o) { return o.day == e.day && strcmp(o.summary, e.summary) == 0; }))
|
||||||
|
continue;
|
||||||
|
if (out.count < MAX_EVENTS) {
|
||||||
|
out.items[out.count++] = e;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// full: keep the nearest events
|
||||||
|
Event *last = std::max_element(out.items, out.items + out.count,
|
||||||
|
[](const Event &a, const Event &b) { return a.day < b.day; });
|
||||||
|
if (e.day < last->day)
|
||||||
|
*last = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::sort(out.items, out.items + out.count, [](const Event &a, const Event &b) {
|
||||||
|
// all-day (0xFFFF wraps to 0) first
|
||||||
|
return a.day != b.day ? a.day < b.day : uint16_t(a.minute + 1) < uint16_t(b.minute + 1);
|
||||||
|
});
|
||||||
|
out.fetched_at = now;
|
||||||
|
ev = out;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// battery cycle: refreshed once a day, after midnight
|
||||||
|
inline bool stale(const esphome::ESPTime &now) {
|
||||||
|
const auto &ev = events();
|
||||||
|
return !ev.valid() || !now.is_valid() || now.timestamp - ev.fetched_at >= 20 * 3600;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline esphome::ESPPreferenceObject &pref() {
|
||||||
|
static esphome::ESPPreferenceObject p = esphome::global_preferences->make_preference<Events>(PREF_HASH, true);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool load(Events &ev) {
|
||||||
|
if (!pref().load(&ev)) {
|
||||||
|
ev = Events{};
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ev.count = std::min(ev.count, MAX_EVENTS);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// written to flash right away: the board may power off before the next preferences flush
|
||||||
|
inline bool save(const Events &ev) { return pref().save(&ev) && esphome::global_preferences->sync(); }
|
||||||
|
|
||||||
|
} // namespace m5paper_calendar
|
||||||
+1
-2
@@ -491,8 +491,7 @@ sensor:
|
|||||||
# name: Dallas Temp 4
|
# name: Dallas Temp 4
|
||||||
# one_wire_id: hub_1
|
# one_wire_id: hub_1
|
||||||
|
|
||||||
time:
|
# time:
|
||||||
- !include common/time/homeassistant.yaml
|
|
||||||
# - platform: sntp
|
# - platform: sntp
|
||||||
# id: sntp_time
|
# id: sntp_time
|
||||||
# timezone: Europe/Bratislava
|
# timezone: Europe/Bratislava
|
||||||
|
|||||||
+1
-2
@@ -139,8 +139,7 @@ switch:
|
|||||||
id: relay_2
|
id: relay_2
|
||||||
|
|
||||||
time:
|
time:
|
||||||
- platform: homeassistant
|
- id: !extend ha_time
|
||||||
timezone: Europe/Bratislava
|
|
||||||
on_time:
|
on_time:
|
||||||
- seconds: 0
|
- seconds: 0
|
||||||
minutes: /15
|
minutes: /15
|
||||||
|
|||||||
@@ -31,11 +31,6 @@ packages:
|
|||||||
|
|
||||||
debug:
|
debug:
|
||||||
|
|
||||||
time:
|
|
||||||
- platform: homeassistant
|
|
||||||
id: ha_time
|
|
||||||
timezone: Europe/Bratislava
|
|
||||||
|
|
||||||
i2c:
|
i2c:
|
||||||
# - id: i2c0
|
# - id: i2c0
|
||||||
sda: 32
|
sda: 32
|
||||||
|
|||||||
+1
-5
@@ -23,6 +23,7 @@ esp32:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
ipv6_only: !include common/ipv6_only.yaml
|
||||||
|
|
||||||
# GPIO21 UART TX
|
# GPIO21 UART TX
|
||||||
# GPIO20 UART RX
|
# GPIO20 UART RX
|
||||||
@@ -109,11 +110,6 @@ globals:
|
|||||||
restore_value: no
|
restore_value: no
|
||||||
initial_value: "false"
|
initial_value: "false"
|
||||||
|
|
||||||
time:
|
|
||||||
- platform: homeassistant
|
|
||||||
id: ha_time
|
|
||||||
timezone: Europe/Bratislava
|
|
||||||
|
|
||||||
display:
|
display:
|
||||||
- platform: tm1637
|
- platform: tm1637
|
||||||
id: tm1637_display
|
id: tm1637_display
|
||||||
|
|||||||
@@ -51,11 +51,6 @@ globals:
|
|||||||
restore_value: no
|
restore_value: no
|
||||||
initial_value: "false"
|
initial_value: "false"
|
||||||
|
|
||||||
time:
|
|
||||||
- platform: homeassistant
|
|
||||||
id: ha_time
|
|
||||||
timezone: Europe/Bratislava
|
|
||||||
|
|
||||||
binary_sensor:
|
binary_sensor:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
name: Button
|
name: Button
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ esp32:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
ipv6_only: !include common/ipv6_only.yaml
|
||||||
|
|
||||||
# Mi Desk Lamp
|
# Mi Desk Lamp
|
||||||
# Pin Function (Name)
|
# Pin Function (Name)
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ esp32:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
ipv6_only: !include common/ipv6_only.yaml
|
||||||
|
|
||||||
# fan:
|
# fan:
|
||||||
# - platform: speed
|
# - platform: speed
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ esp32:
|
|||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
|
||||||
|
external_components:
|
||||||
|
# speaker media player: skip a failed playlist item (esphome#19862), drop once merged
|
||||||
|
- source: github://randybb/esphome-components
|
||||||
|
components: [speaker]
|
||||||
|
refresh: 1h
|
||||||
|
|
||||||
api:
|
api:
|
||||||
actions:
|
actions:
|
||||||
# https://github.com/granadaxronos/120-SONG_NOKIA_RTTTL_RINGTONE_PLAYER_FOR_ARDUINO_UNO/blob/master/RTTTL_PLAYER/songs.h
|
# https://github.com/granadaxronos/120-SONG_NOKIA_RTTTL_RINGTONE_PLAYER_FOR_ARDUINO_UNO/blob/master/RTTTL_PLAYER/songs.h
|
||||||
|
|||||||
+8
-3
@@ -21,6 +21,12 @@ esp32:
|
|||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
|
||||||
|
external_components:
|
||||||
|
# speaker media player: skip a failed playlist item (esphome#19862), drop once merged
|
||||||
|
- source: github://randybb/esphome-components
|
||||||
|
components: [speaker]
|
||||||
|
refresh: 1h
|
||||||
|
|
||||||
api:
|
api:
|
||||||
actions:
|
actions:
|
||||||
# https://github.com/granadaxronos/120-SONG_NOKIA_RTTTL_RINGTONE_PLAYER_FOR_ARDUINO_UNO/blob/master/RTTTL_PLAYER/songs.h
|
# https://github.com/granadaxronos/120-SONG_NOKIA_RTTTL_RINGTONE_PLAYER_FOR_ARDUINO_UNO/blob/master/RTTTL_PLAYER/songs.h
|
||||||
@@ -127,16 +133,15 @@ media_player:
|
|||||||
- platform: speaker
|
- platform: speaker
|
||||||
id: media_out
|
id: media_out
|
||||||
name: Player
|
name: Player
|
||||||
codec_support_enabled: false
|
|
||||||
buffer_size: 14000
|
buffer_size: 14000
|
||||||
media_pipeline:
|
media_pipeline:
|
||||||
speaker: media_spk_resampling_input
|
speaker: media_spk_resampling_input
|
||||||
# format: WAV
|
format: WAV # only the WAV decoder is built (was codec_support_enabled: false)
|
||||||
# num_channels: 1
|
# num_channels: 1
|
||||||
# sample_rate: 8000 # Lower sample rate
|
# sample_rate: 8000 # Lower sample rate
|
||||||
announcement_pipeline:
|
announcement_pipeline:
|
||||||
speaker: announcement_spk_resampling_input
|
speaker: announcement_spk_resampling_input
|
||||||
# format: WAV
|
format: WAV
|
||||||
# num_channels: 1
|
# num_channels: 1
|
||||||
# sample_rate: 8000 # Lower sample rate
|
# sample_rate: 8000 # Lower sample rate
|
||||||
on_play:
|
on_play:
|
||||||
|
|||||||
@@ -242,11 +242,6 @@ binary_sensor:
|
|||||||
# on_clockwise:
|
# on_clockwise:
|
||||||
# - display_menu.down:
|
# - display_menu.down:
|
||||||
|
|
||||||
time:
|
|
||||||
- platform: homeassistant
|
|
||||||
id: ha_time
|
|
||||||
timezone: Europe/Bratislava
|
|
||||||
|
|
||||||
# display:
|
# display:
|
||||||
# - platform: tm1637
|
# - platform: tm1637
|
||||||
# id: tm1637_display
|
# id: tm1637_display
|
||||||
|
|||||||
+1
-5
@@ -27,6 +27,7 @@ external_components:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
ipv6_only: !include common/ipv6_only.yaml
|
||||||
|
|
||||||
|
|
||||||
# PINOUTS: M5ATOM+Atom Hub Switch
|
# PINOUTS: M5ATOM+Atom Hub Switch
|
||||||
@@ -44,11 +45,6 @@ packages:
|
|||||||
# Relay1 G22
|
# Relay1 G22
|
||||||
# Relay2 G19
|
# Relay2 G19
|
||||||
|
|
||||||
time:
|
|
||||||
- platform: homeassistant
|
|
||||||
id: ha_time
|
|
||||||
timezone: Europe/Bratislava
|
|
||||||
|
|
||||||
i2c:
|
i2c:
|
||||||
# - id: i2c0
|
# - id: i2c0
|
||||||
# sda: 26
|
# sda: 26
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ esp32:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
ipv6_only: !include common/ipv6_only.yaml
|
||||||
|
|
||||||
# PINOUTS: M5ATOM
|
# PINOUTS: M5ATOM
|
||||||
# IR G12
|
# IR G12
|
||||||
|
|||||||
+2
-3
@@ -37,6 +37,7 @@ external_components:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
ipv6_only: !include common/ipv6_only.yaml
|
||||||
|
|
||||||
# PINOUTS: M5ATOM
|
# PINOUTS: M5ATOM
|
||||||
# IR G12
|
# IR G12
|
||||||
@@ -129,9 +130,7 @@ globals:
|
|||||||
initial_value: '20.5'
|
initial_value: '20.5'
|
||||||
|
|
||||||
time:
|
time:
|
||||||
- platform: homeassistant
|
- id: !extend ha_time
|
||||||
id: ha_time
|
|
||||||
timezone: Europe/Bratislava
|
|
||||||
on_time:
|
on_time:
|
||||||
- seconds: 0
|
- seconds: 0
|
||||||
minutes: /1
|
minutes: /1
|
||||||
|
|||||||
+1
-5
@@ -36,6 +36,7 @@ esp32:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
ipv6_only: !include common/ipv6_only.yaml
|
||||||
|
|
||||||
api:
|
api:
|
||||||
reboot_timeout: 300s
|
reboot_timeout: 300s
|
||||||
@@ -55,11 +56,6 @@ api:
|
|||||||
# Relay1 G22
|
# Relay1 G22
|
||||||
# Relay2 G19
|
# Relay2 G19
|
||||||
|
|
||||||
time:
|
|
||||||
- platform: homeassistant
|
|
||||||
id: ha_time
|
|
||||||
timezone: Europe/Bratislava
|
|
||||||
|
|
||||||
i2c:
|
i2c:
|
||||||
# - id: i2c0
|
# - id: i2c0
|
||||||
# sda: 26
|
# sda: 26
|
||||||
|
|||||||
@@ -0,0 +1,306 @@
|
|||||||
|
---
|
||||||
|
substitutions:
|
||||||
|
device: lvgl-4848s040
|
||||||
|
name: LVGL 4848S040
|
||||||
|
area: DEV
|
||||||
|
comment: "${area} | Guition ESP32-S3-4848S040"
|
||||||
|
|
||||||
|
esphome:
|
||||||
|
name: mcu-${device}
|
||||||
|
friendly_name: ${name}
|
||||||
|
area: ${area}
|
||||||
|
comment: ${comment}
|
||||||
|
on_boot:
|
||||||
|
- priority: -100
|
||||||
|
then:
|
||||||
|
- lvgl.page.show: status_page
|
||||||
|
|
||||||
|
esp32:
|
||||||
|
variant: esp32s3
|
||||||
|
flash_size: 16MB
|
||||||
|
framework:
|
||||||
|
type: esp-idf
|
||||||
|
# PSRAM bandwidth for the RGB panel (not set by ESPHome); without it the
|
||||||
|
# image shifts during flash/WiFi activity
|
||||||
|
sdkconfig_options:
|
||||||
|
CONFIG_ESP32S3_DATA_CACHE_64KB: y
|
||||||
|
CONFIG_SPIRAM_FETCH_INSTRUCTIONS: y
|
||||||
|
CONFIG_SPIRAM_RODATA: y
|
||||||
|
|
||||||
|
psram:
|
||||||
|
mode: octal
|
||||||
|
speed: 80MHz
|
||||||
|
|
||||||
|
packages:
|
||||||
|
common: !include common/common.yaml
|
||||||
|
ipv6_only: !include common/ipv6_only.yaml
|
||||||
|
|
||||||
|
# PINOUTS: Guition ESP32-S3-4848S040, 4", 480x480, ST7701S, capacitive
|
||||||
|
## Display (RGB, pins in the mipi_rgb model)
|
||||||
|
# CS 39
|
||||||
|
# SCK 48 ST7701S init (3-wire SPI)
|
||||||
|
# SDA 47
|
||||||
|
# DE 18
|
||||||
|
# HSYNC 16
|
||||||
|
# VSYNC 17
|
||||||
|
# PCLK 21
|
||||||
|
# BL 38
|
||||||
|
## Capacitive Touch (GT911)
|
||||||
|
# TP_SDA 19
|
||||||
|
# TP_SCL 45
|
||||||
|
## Relays (86 box variant)
|
||||||
|
# RELAY1 40
|
||||||
|
# RELAY2 2
|
||||||
|
# RELAY3 1
|
||||||
|
|
||||||
|
api:
|
||||||
|
# status page until Home Assistant connects (log viewers don't subscribe to states)
|
||||||
|
on_client_connected:
|
||||||
|
- delay: 2s
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
api.connected:
|
||||||
|
state_subscription_only: true
|
||||||
|
then:
|
||||||
|
- lvgl.page.show: main_page
|
||||||
|
on_client_disconnected:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
not:
|
||||||
|
api.connected:
|
||||||
|
state_subscription_only: true
|
||||||
|
then:
|
||||||
|
- lvgl.page.show: status_page
|
||||||
|
|
||||||
|
i2c:
|
||||||
|
- id: i2c_main
|
||||||
|
sda: 19
|
||||||
|
scl: 45
|
||||||
|
scan: true
|
||||||
|
|
||||||
|
spi:
|
||||||
|
- id: lcd_spi
|
||||||
|
clk_pin: 48
|
||||||
|
mosi_pin: 47
|
||||||
|
|
||||||
|
display:
|
||||||
|
- platform: mipi_rgb
|
||||||
|
model: GUITION-4848S040
|
||||||
|
id: main_display
|
||||||
|
spi_id: lcd_spi
|
||||||
|
update_interval: never
|
||||||
|
auto_clear_enabled: false
|
||||||
|
|
||||||
|
touchscreen:
|
||||||
|
- platform: gt911
|
||||||
|
id: main_touch
|
||||||
|
display: main_display
|
||||||
|
i2c_id: i2c_main
|
||||||
|
# the mipi_rgb model mirrors the image on both axes, touch has to follow
|
||||||
|
transform:
|
||||||
|
mirror_x: true
|
||||||
|
mirror_y: true
|
||||||
|
|
||||||
|
output:
|
||||||
|
- platform: ledc
|
||||||
|
id: gpio_backlight_pwm
|
||||||
|
pin: 38
|
||||||
|
frequency: 1220
|
||||||
|
|
||||||
|
light:
|
||||||
|
- platform: monochromatic
|
||||||
|
id: back_light
|
||||||
|
name: Backlight
|
||||||
|
output: gpio_backlight_pwm
|
||||||
|
restore_mode: ALWAYS_ON
|
||||||
|
|
||||||
|
script:
|
||||||
|
- id: update_status
|
||||||
|
then:
|
||||||
|
- lambda: |-
|
||||||
|
// same colors as the other status screens: ok green, waiting orange, details gray
|
||||||
|
static const uint32_t OK = 0x4DD966, WAIT = 0xFFB300;
|
||||||
|
auto set = [](lv_obj_t *label, const std::string &text, uint32_t color) {
|
||||||
|
lv_label_set_text(label, text.c_str());
|
||||||
|
lv_obj_set_style_text_color(label, lv_color_hex(color), LV_PART_MAIN);
|
||||||
|
};
|
||||||
|
|
||||||
|
const bool wifi_ok = wifi::global_wifi_component->is_connected();
|
||||||
|
set(id(status_wifi),
|
||||||
|
wifi_ok ? str_sprintf("WiFi: connected (%d dBm)", wifi::global_wifi_component->wifi_rssi())
|
||||||
|
: std::string("WiFi: connecting..."),
|
||||||
|
wifi_ok ? OK : WAIT);
|
||||||
|
|
||||||
|
// IPv6 first (skip link-local), then IPv4
|
||||||
|
std::string ips;
|
||||||
|
if (wifi_ok) {
|
||||||
|
char buf[network::IP_ADDRESS_BUFFER_SIZE];
|
||||||
|
for (bool v6 : {true, false}) {
|
||||||
|
for (auto &ip : wifi::global_wifi_component->get_ip_addresses()) {
|
||||||
|
if (!ip.is_set() || ip.is_ip6() != v6)
|
||||||
|
continue;
|
||||||
|
ip.str_to(buf);
|
||||||
|
if (v6 && strncmp(buf, "fe80", 4) == 0)
|
||||||
|
continue;
|
||||||
|
if (!ips.empty())
|
||||||
|
ips += "\n";
|
||||||
|
ips += buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lv_label_set_text(id(status_ips), ips.c_str());
|
||||||
|
if (ips.empty())
|
||||||
|
lv_obj_add_flag(id(status_ips), LV_OBJ_FLAG_HIDDEN);
|
||||||
|
else
|
||||||
|
lv_obj_remove_flag(id(status_ips), LV_OBJ_FLAG_HIDDEN);
|
||||||
|
|
||||||
|
const bool api_ok = api::global_api_server->is_connected();
|
||||||
|
set(id(status_ha), api_ok ? "Home Assistant: connected" : "Home Assistant: waiting...",
|
||||||
|
api_ok ? OK : WAIT);
|
||||||
|
|
||||||
|
const uint32_t up = millis() / 1000;
|
||||||
|
lv_label_set_text(id(status_footer),
|
||||||
|
str_sprintf("ESPHome %s · up %ud %02u:%02u:%02u", ESPHOME_VERSION, up / 86400,
|
||||||
|
(up / 3600) % 24, (up / 60) % 60, up % 60).c_str());
|
||||||
|
|
||||||
|
interval:
|
||||||
|
- interval: 1s
|
||||||
|
then:
|
||||||
|
- lvgl.label.update:
|
||||||
|
id: clock_label
|
||||||
|
text: !lambda |-
|
||||||
|
auto now = id(ha_time).now();
|
||||||
|
if (!now.is_valid())
|
||||||
|
return std::string("--:--");
|
||||||
|
return now.strftime("%H:%M");
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lvgl.page.is_showing: status_page
|
||||||
|
then:
|
||||||
|
- script.execute: update_status
|
||||||
|
|
||||||
|
image:
|
||||||
|
- platform: file
|
||||||
|
file: assets/images/made-for-esphome-white-on-black.svg
|
||||||
|
id: esphome_logo
|
||||||
|
resize: 320x95
|
||||||
|
type: RGB565
|
||||||
|
transparency: alpha_channel
|
||||||
|
|
||||||
|
font:
|
||||||
|
- file: "gfonts://Roboto@500"
|
||||||
|
id: font_clock
|
||||||
|
size: 150
|
||||||
|
glyphs: "0123456789:-"
|
||||||
|
- file: "gfonts://Roboto"
|
||||||
|
id: font_title
|
||||||
|
size: 32
|
||||||
|
- file: "gfonts://Roboto"
|
||||||
|
id: font_status
|
||||||
|
size: 20
|
||||||
|
glyphs: "·"
|
||||||
|
glyphsets: [GF_Latin_Kernel]
|
||||||
|
|
||||||
|
lvgl:
|
||||||
|
log_level: WARN
|
||||||
|
color_depth: 16
|
||||||
|
bg_color: 0x000000
|
||||||
|
text_color: 0xFFFFFF
|
||||||
|
theme:
|
||||||
|
buttonmatrix:
|
||||||
|
bg_opa: TRANSP
|
||||||
|
border_width: 0
|
||||||
|
items:
|
||||||
|
bg_color: 0x2B2F36
|
||||||
|
text_color: 0xFFFFFF
|
||||||
|
radius: 12
|
||||||
|
checked:
|
||||||
|
bg_color: 0xE08A00
|
||||||
|
top_layer:
|
||||||
|
widgets:
|
||||||
|
# same navigation bar as on mcu-lvgl-trainer
|
||||||
|
- buttonmatrix:
|
||||||
|
id: nav_bar
|
||||||
|
align: bottom_mid
|
||||||
|
width: 100%
|
||||||
|
height: 60
|
||||||
|
pad_all: 4
|
||||||
|
pad_column: 6
|
||||||
|
items:
|
||||||
|
text_font: montserrat_28
|
||||||
|
rows:
|
||||||
|
- buttons:
|
||||||
|
- id: page_prev
|
||||||
|
text: ""
|
||||||
|
on_press:
|
||||||
|
then:
|
||||||
|
lvgl.page.previous:
|
||||||
|
- id: page_home
|
||||||
|
text: ""
|
||||||
|
on_press:
|
||||||
|
then:
|
||||||
|
lvgl.page.show: main_page
|
||||||
|
- id: page_next
|
||||||
|
text: ""
|
||||||
|
on_press:
|
||||||
|
then:
|
||||||
|
lvgl.page.next:
|
||||||
|
pages:
|
||||||
|
# 1: clock
|
||||||
|
- id: main_page
|
||||||
|
widgets:
|
||||||
|
- label:
|
||||||
|
id: clock_label
|
||||||
|
align: CENTER
|
||||||
|
y: -30
|
||||||
|
text_font: font_clock
|
||||||
|
text: "--:--"
|
||||||
|
|
||||||
|
# 2: status (shown until Home Assistant connects)
|
||||||
|
- id: status_page
|
||||||
|
on_load:
|
||||||
|
- script.execute: update_status
|
||||||
|
widgets:
|
||||||
|
- image:
|
||||||
|
src: esphome_logo
|
||||||
|
align: TOP_MID
|
||||||
|
y: 16
|
||||||
|
- label:
|
||||||
|
align: TOP_MID
|
||||||
|
y: 120
|
||||||
|
text_font: font_title
|
||||||
|
text: "${name}"
|
||||||
|
- obj:
|
||||||
|
id: status_lines
|
||||||
|
x: 24
|
||||||
|
y: 170
|
||||||
|
width: 440
|
||||||
|
height: SIZE_CONTENT
|
||||||
|
bg_opa: TRANSP
|
||||||
|
border_width: 0
|
||||||
|
pad_all: 0
|
||||||
|
scrollbar_mode: "OFF"
|
||||||
|
layout:
|
||||||
|
type: flex
|
||||||
|
flex_flow: column
|
||||||
|
pad_row: 6
|
||||||
|
widgets:
|
||||||
|
- label:
|
||||||
|
id: status_wifi
|
||||||
|
text_font: font_status
|
||||||
|
text: ""
|
||||||
|
- label:
|
||||||
|
id: status_ips
|
||||||
|
text_font: font_status
|
||||||
|
text_color: 0x8C8C8C
|
||||||
|
pad_left: 20
|
||||||
|
text: ""
|
||||||
|
- label:
|
||||||
|
id: status_ha
|
||||||
|
text_font: font_status
|
||||||
|
text: ""
|
||||||
|
- label:
|
||||||
|
id: status_footer
|
||||||
|
text_font: font_status
|
||||||
|
text_color: 0x8C8C8C
|
||||||
|
text: ""
|
||||||
+611
-150
@@ -4,6 +4,13 @@ substitutions:
|
|||||||
name: Trainer Display
|
name: Trainer Display
|
||||||
area: Basement
|
area: Basement
|
||||||
comment: "${area} | Pain Cave"
|
comment: "${area} | Pain Cave"
|
||||||
|
light_basement: light.mcu_basement_2_light
|
||||||
|
light_workbench: light.mcu_basement_2_workbench_light
|
||||||
|
light_exercise: light.exercise_room
|
||||||
|
fan: light.mcu_dimmer_exercise_room_fan
|
||||||
|
|
||||||
|
# Pages (480x320, navigation bar at the bottom):
|
||||||
|
# clock, lights, training, fan, stopwatch, status
|
||||||
|
|
||||||
esphome:
|
esphome:
|
||||||
name: mcu-${device}
|
name: mcu-${device}
|
||||||
@@ -17,9 +24,9 @@ esp32:
|
|||||||
framework:
|
framework:
|
||||||
type: esp-idf
|
type: esp-idf
|
||||||
|
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
ipv6_only: !include common/ipv6_only.yaml
|
||||||
|
|
||||||
i2c:
|
i2c:
|
||||||
- id: touchscreen_bus
|
- id: touchscreen_bus
|
||||||
@@ -36,71 +43,17 @@ spi:
|
|||||||
clk_pin: 47
|
clk_pin: 47
|
||||||
data_pins: [21, 48, 40, 39]
|
data_pins: [21, 48, 40, 39]
|
||||||
|
|
||||||
color:
|
|
||||||
- id: my_red
|
|
||||||
red: 100%
|
|
||||||
green: 0%
|
|
||||||
blue: 0%
|
|
||||||
- id: my_orange
|
|
||||||
red: 100%
|
|
||||||
green: 50%
|
|
||||||
blue: 0%
|
|
||||||
- id: my_yellow
|
|
||||||
red: 100%
|
|
||||||
green: 100%
|
|
||||||
blue: 0%
|
|
||||||
- id: my_green
|
|
||||||
red: 0%
|
|
||||||
green: 100%
|
|
||||||
blue: 0%
|
|
||||||
- id: my_blue
|
|
||||||
red: 0%
|
|
||||||
green: 0%
|
|
||||||
blue: 100%
|
|
||||||
- id: my_teal
|
|
||||||
red: 0%
|
|
||||||
green: 100%
|
|
||||||
blue: 100%
|
|
||||||
- id: my_gray
|
|
||||||
red: 70%
|
|
||||||
green: 70%
|
|
||||||
blue: 70%
|
|
||||||
- id: my_white
|
|
||||||
red: 100%
|
|
||||||
green: 100%
|
|
||||||
blue: 100%
|
|
||||||
|
|
||||||
font:
|
|
||||||
- file: "gfonts://Roboto"
|
|
||||||
id: Roboto_48
|
|
||||||
size: 48
|
|
||||||
- file: "gfonts://Roboto"
|
|
||||||
id: Roboto_36
|
|
||||||
size: 36
|
|
||||||
- file: "gfonts://Roboto"
|
|
||||||
id: Roboto_24
|
|
||||||
size: 24
|
|
||||||
- file: "gfonts://Roboto"
|
|
||||||
id: Roboto_12
|
|
||||||
size: 12
|
|
||||||
- file: "gfonts://Roboto"
|
|
||||||
id: font_std
|
|
||||||
size: 40
|
|
||||||
glyphs: "!\"%()+=,-_.:°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz/\\[]|&@#'"
|
|
||||||
- file: "gfonts://Roboto@700"
|
|
||||||
id: font_title
|
|
||||||
size: 40
|
|
||||||
glyphs: "!\"%()+=,-_.:°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz/\\[]|&@#'"
|
|
||||||
|
|
||||||
globals:
|
globals:
|
||||||
- id: bgcolor
|
# stopwatch: accumulated time of finished runs + start of the current run
|
||||||
type: Color
|
- id: sw_running
|
||||||
initial_value: "Color::random_color()"
|
type: bool
|
||||||
|
initial_value: 'false'
|
||||||
# power_supply:
|
- id: sw_start
|
||||||
# id: main_backlight
|
type: uint32_t
|
||||||
# pin: 1
|
initial_value: '0'
|
||||||
# enable_on_boot: true
|
- id: sw_accum
|
||||||
|
type: uint32_t
|
||||||
|
initial_value: '0'
|
||||||
|
|
||||||
output:
|
output:
|
||||||
- platform: ledc
|
- platform: ledc
|
||||||
@@ -119,7 +72,6 @@ display:
|
|||||||
- platform: mipi_spi
|
- platform: mipi_spi
|
||||||
model: AXS15231
|
model: AXS15231
|
||||||
data_rate: 40MHz #20mhz is default
|
data_rate: 40MHz #20mhz is default
|
||||||
#spi_mode: MODE2
|
|
||||||
id: main_display
|
id: main_display
|
||||||
spi_id: display_qspi
|
spi_id: display_qspi
|
||||||
dimensions:
|
dimensions:
|
||||||
@@ -127,11 +79,6 @@ display:
|
|||||||
width: 320
|
width: 320
|
||||||
cs_pin: 45
|
cs_pin: 45
|
||||||
auto_clear_enabled: false
|
auto_clear_enabled: false
|
||||||
# show_test_card: true
|
|
||||||
# lambda: |-
|
|
||||||
# it.fill(id(bgcolor));
|
|
||||||
# it.print(it.get_width()/2, it.get_height()/2-20, id(font_title), TextAlign::CENTER, "ESPHome");
|
|
||||||
# it.print(it.get_width()/2, it.get_height()/2+20, id(font_std), TextAlign::CENTER, "@UTBDK");
|
|
||||||
|
|
||||||
touchscreen:
|
touchscreen:
|
||||||
- platform: axs15231
|
- platform: axs15231
|
||||||
@@ -142,27 +89,8 @@ touchscreen:
|
|||||||
# interrupt_pin:
|
# interrupt_pin:
|
||||||
# number: 3
|
# number: 3
|
||||||
# ignore_strapping_warning: true
|
# ignore_strapping_warning: true
|
||||||
calibration:
|
# no calibration/transform: the driver defaults to the native 320x480 panel and
|
||||||
x_min: 0
|
# LVGL applies its own rotation to touch points
|
||||||
x_max: 479
|
|
||||||
y_min: 0
|
|
||||||
y_max: 319
|
|
||||||
transform:
|
|
||||||
swap_xy: true
|
|
||||||
mirror_x: false
|
|
||||||
mirror_y: true
|
|
||||||
# on_touch:
|
|
||||||
# - lambda: |-
|
|
||||||
# Color newColor;
|
|
||||||
# do { newColor = Color::random_color(); } while (newColor == id(bgcolor));
|
|
||||||
# id(bgcolor) = newColor;
|
|
||||||
|
|
||||||
# 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:
|
on_release:
|
||||||
- if:
|
- if:
|
||||||
condition: lvgl.is_paused
|
condition: lvgl.is_paused
|
||||||
@@ -183,107 +111,640 @@ uart:
|
|||||||
|
|
||||||
binary_sensor:
|
binary_sensor:
|
||||||
- platform: homeassistant
|
- platform: homeassistant
|
||||||
id: exercise_room_light
|
id: basement_light
|
||||||
entity_id: light.exercise_room
|
entity_id: ${light_basement}
|
||||||
trigger_on_initial_state: true
|
trigger_on_initial_state: true
|
||||||
on_state:
|
on_state:
|
||||||
then:
|
- lvgl.widget.update:
|
||||||
lvgl.widget.update:
|
id: basement_light_btn
|
||||||
id: exercise_room_light_btn
|
|
||||||
state:
|
state:
|
||||||
checked: !lambda return x;
|
checked: !lambda return x;
|
||||||
- platform: homeassistant
|
- platform: homeassistant
|
||||||
id: exercise_room_fan
|
id: workbench_light
|
||||||
entity_id: light.mcu_dimmer_exercise_room_fan
|
entity_id: ${light_workbench}
|
||||||
trigger_on_initial_state: true
|
trigger_on_initial_state: true
|
||||||
on_state:
|
on_state:
|
||||||
then:
|
- lvgl.widget.update:
|
||||||
lvgl.widget.update:
|
id: workbench_light_btn
|
||||||
id: exercise_room_fan_btn
|
|
||||||
state:
|
state:
|
||||||
checked: !lambda return x;
|
checked: !lambda return x;
|
||||||
|
- platform: homeassistant
|
||||||
|
id: exercise_light
|
||||||
|
entity_id: ${light_exercise}
|
||||||
|
trigger_on_initial_state: true
|
||||||
|
on_state:
|
||||||
|
- lvgl.widget.update:
|
||||||
|
id: exercise_light_btn
|
||||||
|
state:
|
||||||
|
checked: !lambda return x;
|
||||||
|
# brightness and color temperature only make sense while the light is on
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return x;
|
||||||
|
then:
|
||||||
|
- lvgl.widget.show: exercise_controls
|
||||||
|
else:
|
||||||
|
- lvgl.widget.hide: exercise_controls
|
||||||
|
- platform: homeassistant
|
||||||
|
id: fan_on
|
||||||
|
entity_id: ${fan}
|
||||||
|
trigger_on_initial_state: true
|
||||||
|
on_state:
|
||||||
|
- script.execute: update_fan_buttons
|
||||||
|
|
||||||
|
sensor:
|
||||||
|
- platform: homeassistant
|
||||||
|
id: fan_brightness
|
||||||
|
entity_id: ${fan}
|
||||||
|
attribute: brightness
|
||||||
|
on_value:
|
||||||
|
- script.execute: update_fan_buttons
|
||||||
|
|
||||||
|
text_sensor:
|
||||||
|
# light attributes read as text: they are 'None' while the light is off
|
||||||
|
- platform: homeassistant
|
||||||
|
id: exercise_brightness
|
||||||
|
entity_id: ${light_exercise}
|
||||||
|
attribute: brightness
|
||||||
|
on_value:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return parse_number<float>(x).has_value();
|
||||||
|
then:
|
||||||
|
- lvgl.slider.update:
|
||||||
|
id: exercise_brightness_slider
|
||||||
|
value: !lambda return roundf(*parse_number<float>(x) * 100.0f / 255.0f);
|
||||||
|
- platform: homeassistant
|
||||||
|
id: exercise_color_temp
|
||||||
|
entity_id: ${light_exercise}
|
||||||
|
attribute: color_temp_kelvin
|
||||||
|
on_value:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return parse_number<float>(x).has_value();
|
||||||
|
then:
|
||||||
|
- lvgl.slider.update:
|
||||||
|
id: exercise_color_temp_slider
|
||||||
|
value: !lambda return *parse_number<float>(x);
|
||||||
|
# a running automation (e.g. its delay) has current > 0
|
||||||
|
- platform: homeassistant
|
||||||
|
id: training_start_running
|
||||||
|
entity_id: automation.zwift_enable_fan
|
||||||
|
attribute: current
|
||||||
|
on_value:
|
||||||
|
- script.execute: update_training_status
|
||||||
|
- platform: homeassistant
|
||||||
|
id: training_stop_running
|
||||||
|
entity_id: automation.zwift_disable_fan
|
||||||
|
attribute: current
|
||||||
|
on_value:
|
||||||
|
- script.execute: update_training_status
|
||||||
|
|
||||||
|
script:
|
||||||
|
- id: update_fan_buttons
|
||||||
|
then:
|
||||||
|
- lambda: |-
|
||||||
|
// fan level rounded to the 10 % steps of the buttons, 0 = off
|
||||||
|
int pct = 0;
|
||||||
|
if (id(fan_on).state && !std::isnan(id(fan_brightness).state))
|
||||||
|
pct = (int) roundf(id(fan_brightness).state * 10.0f / 255.0f) * 10;
|
||||||
|
const int levels[] = {0, 50, 60, 70, 80, 90, 100};
|
||||||
|
lv_obj_t *matrix = id(fan_matrix)->obj;
|
||||||
|
for (int i = 0; i < 7; i++) {
|
||||||
|
if (levels[i] == pct)
|
||||||
|
lv_buttonmatrix_set_button_ctrl(matrix, i, LV_BUTTONMATRIX_CTRL_CHECKED);
|
||||||
|
else
|
||||||
|
lv_buttonmatrix_clear_button_ctrl(matrix, i, LV_BUTTONMATRIX_CTRL_CHECKED);
|
||||||
|
}
|
||||||
|
- lvgl.label.update:
|
||||||
|
id: fan_status_label
|
||||||
|
text: !lambda |-
|
||||||
|
if (!id(fan_on).state)
|
||||||
|
return std::string("Ventilátor vypnutý");
|
||||||
|
if (std::isnan(id(fan_brightness).state))
|
||||||
|
return std::string("Ventilátor zapnutý");
|
||||||
|
return str_sprintf("Ventilátor %.0f %%", id(fan_brightness).state * 100.0f / 255.0f);
|
||||||
|
|
||||||
|
- id: update_training_status
|
||||||
|
then:
|
||||||
|
- lvgl.label.update:
|
||||||
|
id: training_status_label
|
||||||
|
text: !lambda |-
|
||||||
|
if (id(training_start_running).state != "0" && !id(training_start_running).state.empty())
|
||||||
|
return std::string("Tréning sa spúšťa, ventilátor sa zapne o 10 min");
|
||||||
|
if (id(training_stop_running).state != "0" && !id(training_stop_running).state.empty())
|
||||||
|
return std::string("Tréning sa ukončuje, ventilátor sa vypne o 2 min");
|
||||||
|
return std::string("");
|
||||||
|
|
||||||
|
- id: update_stopwatch
|
||||||
|
then:
|
||||||
|
- lvgl.label.update:
|
||||||
|
id: stopwatch_label
|
||||||
|
text: !lambda |-
|
||||||
|
const unsigned ms = id(sw_accum) + (id(sw_running) ? millis() - id(sw_start) : 0);
|
||||||
|
const unsigned s = ms / 1000;
|
||||||
|
if (s >= 3600)
|
||||||
|
return str_sprintf("%u:%02u:%02u", s / 3600, (s / 60) % 60, s % 60);
|
||||||
|
return str_sprintf("%02u:%02u.%u", s / 60, s % 60, (ms / 100) % 10);
|
||||||
|
- lvgl.label.update:
|
||||||
|
id: stopwatch_start_label
|
||||||
|
text: !lambda |-
|
||||||
|
return std::string(id(sw_running) ? "Stop" : "Štart");
|
||||||
|
|
||||||
|
- id: update_status
|
||||||
|
then:
|
||||||
|
- lambda: |-
|
||||||
|
// same colors as the other status screens: ok green, waiting orange, details gray
|
||||||
|
static const uint32_t OK = 0x4DD966, WAIT = 0xFFB300;
|
||||||
|
auto set = [](lv_obj_t *label, const std::string &text, uint32_t color) {
|
||||||
|
lv_label_set_text(label, text.c_str());
|
||||||
|
lv_obj_set_style_text_color(label, lv_color_hex(color), LV_PART_MAIN);
|
||||||
|
};
|
||||||
|
|
||||||
|
const bool wifi_ok = wifi::global_wifi_component->is_connected();
|
||||||
|
set(id(status_wifi),
|
||||||
|
wifi_ok ? str_sprintf("WiFi: connected (%d dBm)", wifi::global_wifi_component->wifi_rssi())
|
||||||
|
: std::string("WiFi: connecting..."),
|
||||||
|
wifi_ok ? OK : WAIT);
|
||||||
|
|
||||||
|
// IPv6 first (skip link-local), then IPv4
|
||||||
|
std::string ips;
|
||||||
|
if (wifi_ok) {
|
||||||
|
char buf[network::IP_ADDRESS_BUFFER_SIZE];
|
||||||
|
for (bool v6 : {true, false}) {
|
||||||
|
for (auto &ip : wifi::global_wifi_component->get_ip_addresses()) {
|
||||||
|
if (!ip.is_set() || ip.is_ip6() != v6)
|
||||||
|
continue;
|
||||||
|
ip.str_to(buf);
|
||||||
|
if (v6 && strncmp(buf, "fe80", 4) == 0)
|
||||||
|
continue;
|
||||||
|
if (!ips.empty())
|
||||||
|
ips += "\n";
|
||||||
|
ips += buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lv_label_set_text(id(status_ips), ips.c_str());
|
||||||
|
if (ips.empty())
|
||||||
|
lv_obj_add_flag(id(status_ips), LV_OBJ_FLAG_HIDDEN);
|
||||||
|
else
|
||||||
|
lv_obj_remove_flag(id(status_ips), LV_OBJ_FLAG_HIDDEN);
|
||||||
|
|
||||||
|
const bool api_ok = api::global_api_server->is_connected();
|
||||||
|
set(id(status_ha), api_ok ? "Home Assistant: connected" : "Home Assistant: waiting...",
|
||||||
|
api_ok ? OK : WAIT);
|
||||||
|
|
||||||
|
const uint32_t up = millis() / 1000;
|
||||||
|
lv_label_set_text(id(status_footer),
|
||||||
|
str_sprintf("ESPHome %s · up %ud %02u:%02u:%02u", ESPHOME_VERSION, up / 86400,
|
||||||
|
(up / 3600) % 24, (up / 60) % 60, up % 60).c_str());
|
||||||
|
interval:
|
||||||
|
- interval: 1s
|
||||||
|
then:
|
||||||
|
- lvgl.label.update:
|
||||||
|
id: clock_label
|
||||||
|
text: !lambda |-
|
||||||
|
auto now = id(ha_time).now();
|
||||||
|
if (!now.is_valid())
|
||||||
|
return std::string("--:--:--");
|
||||||
|
return now.strftime("%H:%M:%S");
|
||||||
|
- interval: 100ms
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
and:
|
||||||
|
- lambda: return id(sw_running);
|
||||||
|
- lvgl.page.is_showing: stopwatch_page
|
||||||
|
then:
|
||||||
|
- script.execute: update_stopwatch
|
||||||
|
- interval: 2s
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lvgl.page.is_showing: status_page
|
||||||
|
then:
|
||||||
|
- script.execute: update_status
|
||||||
|
|
||||||
|
image:
|
||||||
|
- platform: file
|
||||||
|
file: assets/images/made-for-esphome-white-on-black.svg
|
||||||
|
id: esphome_logo
|
||||||
|
resize: 280x83
|
||||||
|
type: RGB565
|
||||||
|
transparency: alpha_channel
|
||||||
|
|
||||||
|
font:
|
||||||
|
- file: "gfonts://Roboto@500"
|
||||||
|
id: font_digits
|
||||||
|
size: 110
|
||||||
|
glyphs: "0123456789:.-"
|
||||||
|
- file: "gfonts://Roboto"
|
||||||
|
id: font_ui
|
||||||
|
size: 24
|
||||||
|
glyphs: &sk_glyphs "ÁÄČĎÉÍĹĽŇÓÔŔŠŤÚÝŽáäčďéíĺľňóôŕšťúýž°·"
|
||||||
|
glyphsets: [GF_Latin_Kernel]
|
||||||
|
- file: "gfonts://Roboto@500"
|
||||||
|
id: font_button
|
||||||
|
size: 34
|
||||||
|
glyphs: *sk_glyphs
|
||||||
|
glyphsets: [GF_Latin_Kernel]
|
||||||
|
- file: "gfonts://Roboto"
|
||||||
|
id: font_small
|
||||||
|
size: 18
|
||||||
|
glyphs: *sk_glyphs
|
||||||
|
glyphsets: [GF_Latin_Kernel]
|
||||||
|
|
||||||
lvgl:
|
lvgl:
|
||||||
rotation: 90
|
rotation: 90
|
||||||
buffer_size: 100%
|
buffer_size: 100%
|
||||||
log_level: WARN
|
log_level: WARN
|
||||||
color_depth: 16
|
color_depth: 16
|
||||||
default_font: montserrat_18
|
default_font: font_ui
|
||||||
# on_idle:
|
bg_color: 0x000000
|
||||||
# - timeout: !lambda "return (id(display_timeout).state * 1000);"
|
text_color: 0xFFFFFF
|
||||||
# then:
|
theme:
|
||||||
# - logger.log: "LVGL is idle"
|
button:
|
||||||
# - light.turn_off:
|
bg_color: 0x2B2F36
|
||||||
# id: display_backlight
|
radius: 12
|
||||||
# transition_length: 2500ms
|
checked:
|
||||||
# - lvgl.pause:
|
bg_color: 0xE08A00
|
||||||
|
buttonmatrix:
|
||||||
|
bg_opa: TRANSP
|
||||||
|
border_width: 0
|
||||||
|
items:
|
||||||
|
bg_color: 0x2B2F36
|
||||||
|
text_color: 0xFFFFFF
|
||||||
|
radius: 12
|
||||||
|
checked:
|
||||||
|
bg_color: 0xE08A00
|
||||||
|
slider:
|
||||||
|
bg_color: 0x2B2F36
|
||||||
|
indicator:
|
||||||
|
bg_color: 0xE08A00
|
||||||
|
knob:
|
||||||
|
bg_color: 0xFFFFFF
|
||||||
top_layer:
|
top_layer:
|
||||||
widgets:
|
widgets:
|
||||||
- buttonmatrix:
|
- buttonmatrix:
|
||||||
|
id: nav_bar
|
||||||
align: bottom_mid
|
align: bottom_mid
|
||||||
# styles: header_footer
|
width: 100%
|
||||||
pad_all: 0
|
height: 56
|
||||||
outline_width: 0
|
pad_all: 4
|
||||||
id: top_layer
|
pad_column: 6
|
||||||
# items:
|
items:
|
||||||
# styles: header_footer
|
text_font: montserrat_28
|
||||||
rows:
|
rows:
|
||||||
- buttons:
|
- buttons:
|
||||||
- id: page_prev
|
- id: page_prev
|
||||||
text: "\uF053"
|
text: ""
|
||||||
on_press:
|
on_press:
|
||||||
then:
|
then:
|
||||||
lvgl.page.previous:
|
lvgl.page.previous:
|
||||||
- id: page_home
|
- id: page_home
|
||||||
text: "\uF015"
|
text: ""
|
||||||
on_press:
|
on_press:
|
||||||
then:
|
then:
|
||||||
lvgl.page.show: main_page
|
lvgl.page.show: clock_page
|
||||||
- id: page_next
|
- id: page_next
|
||||||
text: "\uF054"
|
text: ""
|
||||||
on_press:
|
on_press:
|
||||||
then:
|
then:
|
||||||
lvgl.page.next:
|
lvgl.page.next:
|
||||||
pages:
|
pages:
|
||||||
- id: main_page
|
# 1: clock (default after boot)
|
||||||
|
- id: clock_page
|
||||||
widgets:
|
widgets:
|
||||||
- label:
|
- label:
|
||||||
text: "ESPHome LVGL Display"
|
id: clock_label
|
||||||
align: CENTER
|
align: CENTER
|
||||||
text_align: CENTER
|
y: -28
|
||||||
text_color: 0xFFFFFF
|
text_font: font_digits
|
||||||
- id: excercise_page
|
text: "--:--:--"
|
||||||
|
|
||||||
|
# 2: basement lights
|
||||||
|
- id: lights_page
|
||||||
|
pad_all: 10
|
||||||
widgets:
|
widgets:
|
||||||
- label:
|
|
||||||
align: CENTER
|
|
||||||
text: 'Hello World!'
|
|
||||||
- button:
|
- button:
|
||||||
id: exercise_room_light_btn
|
id: basement_light_btn
|
||||||
align: TOP_LEFT
|
align: TOP_LEFT
|
||||||
width: 100
|
width: 148
|
||||||
height: 70
|
height: 80
|
||||||
checkable: true
|
checkable: true
|
||||||
widgets:
|
widgets:
|
||||||
- label:
|
- label:
|
||||||
align: CENTER
|
align: CENTER
|
||||||
text: 'Light'
|
text: "Pivnica"
|
||||||
on_click:
|
on_click:
|
||||||
- homeassistant.action:
|
- homeassistant.action:
|
||||||
action: light.toggle
|
action: light.toggle
|
||||||
data:
|
data:
|
||||||
entity_id: light.exercise_room
|
entity_id: ${light_basement}
|
||||||
- button:
|
- button:
|
||||||
id: exercise_room_fan_btn
|
id: workbench_light_btn
|
||||||
align: TOP_RIGHT
|
align: TOP_MID
|
||||||
width: 100
|
width: 148
|
||||||
height: 70
|
height: 80
|
||||||
checkable: true
|
checkable: true
|
||||||
widgets:
|
widgets:
|
||||||
- label:
|
- label:
|
||||||
align: CENTER
|
align: CENTER
|
||||||
text: 'Fan'
|
text: "Pracovný stôl"
|
||||||
on_click:
|
on_click:
|
||||||
- homeassistant.action:
|
- homeassistant.action:
|
||||||
action: light.toggle
|
action: light.toggle
|
||||||
data:
|
data:
|
||||||
entity_id: light.mcu_dimmer_exercise_room_fan
|
entity_id: ${light_workbench}
|
||||||
|
- button:
|
||||||
|
id: exercise_light_btn
|
||||||
|
align: TOP_RIGHT
|
||||||
|
width: 148
|
||||||
|
height: 80
|
||||||
|
checkable: true
|
||||||
|
widgets:
|
||||||
|
- label:
|
||||||
|
align: CENTER
|
||||||
|
text: "Posilka"
|
||||||
|
on_click:
|
||||||
|
- homeassistant.action:
|
||||||
|
action: light.toggle
|
||||||
|
data:
|
||||||
|
entity_id: ${light_exercise}
|
||||||
|
- obj:
|
||||||
|
id: exercise_controls
|
||||||
|
hidden: true
|
||||||
|
y: 110
|
||||||
|
width: 460
|
||||||
|
height: 120
|
||||||
|
pad_all: 0
|
||||||
|
bg_opa: TRANSP
|
||||||
|
border_width: 0
|
||||||
|
scrollbar_mode: "OFF"
|
||||||
|
widgets:
|
||||||
|
- label:
|
||||||
|
x: 4
|
||||||
|
y: 0
|
||||||
|
text_font: font_small
|
||||||
|
text: "Posilka jas"
|
||||||
|
- slider:
|
||||||
|
id: exercise_brightness_slider
|
||||||
|
x: 14
|
||||||
|
y: 32
|
||||||
|
width: 430
|
||||||
|
height: 16
|
||||||
|
min_value: 1
|
||||||
|
max_value: 100
|
||||||
|
value: 100
|
||||||
|
on_release:
|
||||||
|
- homeassistant.action:
|
||||||
|
action: light.turn_on
|
||||||
|
data:
|
||||||
|
entity_id: ${light_exercise}
|
||||||
|
brightness_pct: !lambda return (int) x;
|
||||||
|
- label:
|
||||||
|
x: 4
|
||||||
|
y: 62
|
||||||
|
text_font: font_small
|
||||||
|
text: "Posilka teplota (2700 – 6500 K)"
|
||||||
|
- slider:
|
||||||
|
id: exercise_color_temp_slider
|
||||||
|
x: 14
|
||||||
|
y: 94
|
||||||
|
width: 430
|
||||||
|
height: 16
|
||||||
|
min_value: 2700
|
||||||
|
max_value: 6500
|
||||||
|
value: 2700
|
||||||
|
on_release:
|
||||||
|
- homeassistant.action:
|
||||||
|
action: light.turn_on
|
||||||
|
data:
|
||||||
|
entity_id: ${light_exercise}
|
||||||
|
color_temp_kelvin: !lambda return (int) x;
|
||||||
|
|
||||||
|
# 3: training start / stop
|
||||||
|
- id: training_page
|
||||||
|
pad_all: 10
|
||||||
|
widgets:
|
||||||
|
- button:
|
||||||
|
align: TOP_LEFT
|
||||||
|
y: 10
|
||||||
|
width: 222
|
||||||
|
height: 150
|
||||||
|
bg_color: 0x1E7D32
|
||||||
|
widgets:
|
||||||
|
- label:
|
||||||
|
align: CENTER
|
||||||
|
text_font: font_button
|
||||||
|
text: "Štart"
|
||||||
|
on_click:
|
||||||
|
- homeassistant.action:
|
||||||
|
action: automation.trigger
|
||||||
|
data:
|
||||||
|
entity_id: automation.zwift_enable_fan
|
||||||
|
- button:
|
||||||
|
align: TOP_RIGHT
|
||||||
|
y: 10
|
||||||
|
width: 222
|
||||||
|
height: 150
|
||||||
|
bg_color: 0xB71C1C
|
||||||
|
widgets:
|
||||||
|
- label:
|
||||||
|
align: CENTER
|
||||||
|
text_font: font_button
|
||||||
|
text: "Stop"
|
||||||
|
on_click:
|
||||||
|
- homeassistant.action:
|
||||||
|
action: automation.trigger
|
||||||
|
data:
|
||||||
|
entity_id: automation.zwift_disable_fan
|
||||||
|
- label:
|
||||||
|
id: training_status_label
|
||||||
|
align: TOP_MID
|
||||||
|
y: 185
|
||||||
|
width: 440
|
||||||
|
text_align: CENTER
|
||||||
|
text_font: font_small
|
||||||
|
text: ""
|
||||||
|
|
||||||
|
# 4: fan
|
||||||
|
- id: fan_page
|
||||||
|
pad_all: 10
|
||||||
|
widgets:
|
||||||
|
- buttonmatrix:
|
||||||
|
id: fan_matrix
|
||||||
|
align: TOP_MID
|
||||||
|
width: 460
|
||||||
|
height: 180
|
||||||
|
pad_row: 8
|
||||||
|
pad_column: 8
|
||||||
|
one_checked: true
|
||||||
|
items:
|
||||||
|
text_font: font_button
|
||||||
|
rows:
|
||||||
|
- buttons:
|
||||||
|
- text: "Vyp"
|
||||||
|
control:
|
||||||
|
checkable: true
|
||||||
|
on_press:
|
||||||
|
- homeassistant.action:
|
||||||
|
action: light.turn_off
|
||||||
|
data:
|
||||||
|
entity_id: ${fan}
|
||||||
|
- text: "50"
|
||||||
|
control:
|
||||||
|
checkable: true
|
||||||
|
on_press:
|
||||||
|
- homeassistant.action:
|
||||||
|
action: light.turn_on
|
||||||
|
data:
|
||||||
|
entity_id: ${fan}
|
||||||
|
brightness_pct: "50"
|
||||||
|
- text: "60"
|
||||||
|
control:
|
||||||
|
checkable: true
|
||||||
|
on_press:
|
||||||
|
- homeassistant.action:
|
||||||
|
action: light.turn_on
|
||||||
|
data:
|
||||||
|
entity_id: ${fan}
|
||||||
|
brightness_pct: "60"
|
||||||
|
- text: "70"
|
||||||
|
control:
|
||||||
|
checkable: true
|
||||||
|
on_press:
|
||||||
|
- homeassistant.action:
|
||||||
|
action: light.turn_on
|
||||||
|
data:
|
||||||
|
entity_id: ${fan}
|
||||||
|
brightness_pct: "70"
|
||||||
|
- buttons:
|
||||||
|
- text: "80"
|
||||||
|
control:
|
||||||
|
checkable: true
|
||||||
|
on_press:
|
||||||
|
- homeassistant.action:
|
||||||
|
action: light.turn_on
|
||||||
|
data:
|
||||||
|
entity_id: ${fan}
|
||||||
|
brightness_pct: "80"
|
||||||
|
- text: "90"
|
||||||
|
control:
|
||||||
|
checkable: true
|
||||||
|
on_press:
|
||||||
|
- homeassistant.action:
|
||||||
|
action: light.turn_on
|
||||||
|
data:
|
||||||
|
entity_id: ${fan}
|
||||||
|
brightness_pct: "90"
|
||||||
|
- text: "100"
|
||||||
|
control:
|
||||||
|
checkable: true
|
||||||
|
on_press:
|
||||||
|
- homeassistant.action:
|
||||||
|
action: light.turn_on
|
||||||
|
data:
|
||||||
|
entity_id: ${fan}
|
||||||
|
brightness_pct: "100"
|
||||||
|
- label:
|
||||||
|
id: fan_status_label
|
||||||
|
align: TOP_MID
|
||||||
|
y: 200
|
||||||
|
text_font: font_small
|
||||||
|
text: ""
|
||||||
|
|
||||||
|
# 5: stopwatch
|
||||||
|
- id: stopwatch_page
|
||||||
|
pad_all: 10
|
||||||
|
widgets:
|
||||||
|
- label:
|
||||||
|
id: stopwatch_label
|
||||||
|
align: TOP_MID
|
||||||
|
y: 10
|
||||||
|
text_font: font_digits
|
||||||
|
text: "00:00.0"
|
||||||
|
- button:
|
||||||
|
align: TOP_LEFT
|
||||||
|
y: 150
|
||||||
|
width: 222
|
||||||
|
height: 90
|
||||||
|
widgets:
|
||||||
|
- label:
|
||||||
|
id: stopwatch_start_label
|
||||||
|
align: CENTER
|
||||||
|
text_font: font_button
|
||||||
|
text: "Štart"
|
||||||
|
on_click:
|
||||||
|
- lambda: |-
|
||||||
|
if (id(sw_running)) {
|
||||||
|
id(sw_accum) += millis() - id(sw_start);
|
||||||
|
id(sw_running) = false;
|
||||||
|
} else {
|
||||||
|
id(sw_start) = millis();
|
||||||
|
id(sw_running) = true;
|
||||||
|
}
|
||||||
|
- script.execute: update_stopwatch
|
||||||
|
- button:
|
||||||
|
align: TOP_RIGHT
|
||||||
|
y: 150
|
||||||
|
width: 222
|
||||||
|
height: 90
|
||||||
|
widgets:
|
||||||
|
- label:
|
||||||
|
align: CENTER
|
||||||
|
text_font: font_button
|
||||||
|
text: "Reset"
|
||||||
|
on_click:
|
||||||
|
- lambda: |-
|
||||||
|
id(sw_accum) = 0;
|
||||||
|
id(sw_start) = millis();
|
||||||
|
- script.execute: update_stopwatch
|
||||||
|
|
||||||
|
# 6: status
|
||||||
|
- id: status_page
|
||||||
|
on_load:
|
||||||
|
- script.execute: update_status
|
||||||
|
widgets:
|
||||||
|
- image:
|
||||||
|
src: esphome_logo
|
||||||
|
align: TOP_MID
|
||||||
|
y: 8
|
||||||
|
- label:
|
||||||
|
align: TOP_MID
|
||||||
|
y: 96
|
||||||
|
text: "${name}"
|
||||||
|
- obj:
|
||||||
|
id: status_lines
|
||||||
|
x: 20
|
||||||
|
y: 126
|
||||||
|
width: 440
|
||||||
|
height: SIZE_CONTENT
|
||||||
|
bg_opa: TRANSP
|
||||||
|
border_width: 0
|
||||||
|
pad_all: 0
|
||||||
|
scrollbar_mode: "OFF"
|
||||||
|
layout:
|
||||||
|
type: flex
|
||||||
|
flex_flow: column
|
||||||
|
pad_row: 4
|
||||||
|
widgets:
|
||||||
|
- label:
|
||||||
|
id: status_wifi
|
||||||
|
text_font: font_small
|
||||||
|
text: ""
|
||||||
|
- label:
|
||||||
|
id: status_ips
|
||||||
|
text_font: font_small
|
||||||
|
text_color: 0x8C8C8C
|
||||||
|
pad_left: 16
|
||||||
|
text: ""
|
||||||
|
- label:
|
||||||
|
id: status_ha
|
||||||
|
text_font: font_small
|
||||||
|
text: ""
|
||||||
|
- label:
|
||||||
|
id: status_footer
|
||||||
|
text_font: font_small
|
||||||
|
text_color: 0x8C8C8C
|
||||||
|
text: ""
|
||||||
+218
-545
@@ -10,8 +10,10 @@ esphome:
|
|||||||
friendly_name: ${name}
|
friendly_name: ${name}
|
||||||
area: ${area}
|
area: ${area}
|
||||||
comment: ${comment}
|
comment: ${comment}
|
||||||
platformio_options:
|
on_boot:
|
||||||
board_build.flash_mode: dio
|
- priority: -100
|
||||||
|
then:
|
||||||
|
- lvgl.page.show: status_page
|
||||||
|
|
||||||
esp32:
|
esp32:
|
||||||
variant: esp32s3
|
variant: esp32s3
|
||||||
@@ -19,9 +21,9 @@ esp32:
|
|||||||
toolchain: esp-idf
|
toolchain: esp-idf
|
||||||
framework:
|
framework:
|
||||||
type: esp-idf
|
type: esp-idf
|
||||||
# Required to achieve sufficient PSRAM bandwidth
|
# PSRAM bandwidth for the RGB panel (not set by ESPHome); without it the
|
||||||
|
# image shifts during flash/WiFi activity
|
||||||
sdkconfig_options:
|
sdkconfig_options:
|
||||||
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240: y
|
|
||||||
CONFIG_ESP32S3_DATA_CACHE_64KB: y
|
CONFIG_ESP32S3_DATA_CACHE_64KB: y
|
||||||
CONFIG_SPIRAM_FETCH_INSTRUCTIONS: y
|
CONFIG_SPIRAM_FETCH_INSTRUCTIONS: y
|
||||||
CONFIG_SPIRAM_RODATA: y
|
CONFIG_SPIRAM_RODATA: y
|
||||||
@@ -32,6 +34,7 @@ psram:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
ipv6_only: !include common/ipv6_only.yaml
|
||||||
|
|
||||||
# PINOUTS: ESP32 Cheap Yellow Display, 4.3", IPS, Capacitive
|
# PINOUTS: ESP32 Cheap Yellow Display, 4.3", IPS, Capacitive
|
||||||
## Display
|
## Display
|
||||||
@@ -63,6 +66,7 @@ packages:
|
|||||||
# HSYNC 39
|
# HSYNC 39
|
||||||
# VSYNC 41
|
# VSYNC 41
|
||||||
# DE 40
|
# DE 40
|
||||||
|
# BL 2
|
||||||
## Capacitive Touch
|
## Capacitive Touch
|
||||||
# TP_INT
|
# TP_INT
|
||||||
# TP_RST 38
|
# TP_RST 38
|
||||||
@@ -89,579 +93,248 @@ packages:
|
|||||||
# 3 19 TP_SDA
|
# 3 19 TP_SDA
|
||||||
# 4 20 TP_SCL
|
# 4 20 TP_SCL
|
||||||
|
|
||||||
|
api:
|
||||||
|
# status page until Home Assistant connects (log viewers don't subscribe to states)
|
||||||
|
on_client_connected:
|
||||||
|
- delay: 2s
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
api.connected:
|
||||||
|
state_subscription_only: true
|
||||||
|
then:
|
||||||
|
- lvgl.page.show: main_page
|
||||||
|
on_client_disconnected:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
not:
|
||||||
|
api.connected:
|
||||||
|
state_subscription_only: true
|
||||||
|
then:
|
||||||
|
- lvgl.page.show: status_page
|
||||||
|
|
||||||
i2c:
|
i2c:
|
||||||
- id: i2c_main
|
- id: i2c_main
|
||||||
sda: 19
|
sda: 19
|
||||||
scl: 20
|
scl: 20
|
||||||
scan: true
|
scan: true
|
||||||
|
|
||||||
|
# same pins and timing as the 5" Sunton board, pclk lowered for the 4.3" panel
|
||||||
display:
|
display:
|
||||||
- platform: rpi_dpi_rgb
|
- platform: mipi_rgb
|
||||||
|
model: ESP32-8048S050
|
||||||
id: main_display
|
id: main_display
|
||||||
color_order: RGB
|
color_order: RGB
|
||||||
invert_colors: true
|
invert_colors: true
|
||||||
|
pclk_frequency: 12.5MHz
|
||||||
update_interval: never
|
update_interval: never
|
||||||
auto_clear_enabled: false # takes 2.8 seconds to clear the display
|
auto_clear_enabled: false # takes 2.8 seconds to clear the display
|
||||||
dimensions:
|
|
||||||
width: 800
|
|
||||||
height: 480
|
|
||||||
de_pin: 40
|
|
||||||
hsync_pin: 39
|
|
||||||
vsync_pin: 41
|
|
||||||
pclk_pin: 42
|
|
||||||
pclk_frequency: 12.5MHz
|
|
||||||
hsync_pulse_width: 4
|
|
||||||
hsync_front_porch: 8
|
|
||||||
hsync_back_porch: 8
|
|
||||||
vsync_pulse_width: 4
|
|
||||||
vsync_front_porch: 8
|
|
||||||
vsync_back_porch: 8
|
|
||||||
data_pins:
|
|
||||||
red:
|
|
||||||
- 45 # R1
|
|
||||||
- 48 # R2
|
|
||||||
- 47 # R3
|
|
||||||
- 21 # R4
|
|
||||||
- 14 # R5
|
|
||||||
green:
|
|
||||||
- 5 # G0
|
|
||||||
- 6 # G1
|
|
||||||
- 7 # G2
|
|
||||||
- 15 # G3
|
|
||||||
- 16 # G4
|
|
||||||
- 4 # G5
|
|
||||||
blue:
|
|
||||||
- 8 # B1
|
|
||||||
- 3 # B2
|
|
||||||
- 46 # B3
|
|
||||||
- 9 # B4
|
|
||||||
- 1 # B5
|
|
||||||
|
|
||||||
|
touchscreen:
|
||||||
|
- platform: gt911
|
||||||
|
id: main_touch
|
||||||
|
display: main_display
|
||||||
|
i2c_id: i2c_main
|
||||||
|
reset_pin: 38
|
||||||
|
|
||||||
# Define a PWM output on the ESP32
|
|
||||||
output:
|
output:
|
||||||
- platform: ledc
|
- platform: ledc
|
||||||
|
id: gpio_backlight_pwm
|
||||||
pin: 2
|
pin: 2
|
||||||
frequency: 1220
|
frequency: 1220
|
||||||
id: gpio_backlight_pwm
|
|
||||||
|
|
||||||
light:
|
light:
|
||||||
- platform: monochromatic
|
- platform: monochromatic
|
||||||
output: gpio_backlight_pwm
|
|
||||||
id: back_light
|
id: back_light
|
||||||
|
name: Backlight
|
||||||
|
output: gpio_backlight_pwm
|
||||||
restore_mode: ALWAYS_ON
|
restore_mode: ALWAYS_ON
|
||||||
|
|
||||||
text_sensor:
|
|
||||||
- platform: template
|
|
||||||
# name: Uptime Human Readable
|
|
||||||
id: uptime_human
|
|
||||||
icon: mdi:clock-start
|
|
||||||
internal: True
|
|
||||||
|
|
||||||
time:
|
|
||||||
- platform: homeassistant
|
|
||||||
id: time_comp
|
|
||||||
|
|
||||||
number:
|
|
||||||
- platform: template
|
|
||||||
initial_value: 0
|
|
||||||
id: counting_number
|
|
||||||
internal: True
|
|
||||||
min_value: -10
|
|
||||||
max_value: 10
|
|
||||||
step: 1
|
|
||||||
optimistic: True
|
|
||||||
|
|
||||||
# debug:
|
|
||||||
# update_interval: 5s
|
|
||||||
|
|
||||||
sensor:
|
|
||||||
- platform: uptime
|
|
||||||
id: uptime_counter
|
|
||||||
update_interval: 1s
|
|
||||||
accuracy_decimals: 0
|
|
||||||
on_raw_value:
|
|
||||||
then:
|
|
||||||
- light.turn_on:
|
|
||||||
id: back_light
|
|
||||||
brightness: 90%
|
|
||||||
- number.increment:
|
|
||||||
id: counting_number
|
|
||||||
cycle: True
|
|
||||||
- script.execute: update_display
|
|
||||||
# - platform: debug
|
|
||||||
# free:
|
|
||||||
# name: "${name} Heap Free"
|
|
||||||
# block:
|
|
||||||
# name: "${name} Heap Max Block"
|
|
||||||
# loop_time:
|
|
||||||
# name: "${name} Loop Time"
|
|
||||||
# psram:
|
|
||||||
# name: "${name} Free PSRAM"
|
|
||||||
- platform: uptime
|
|
||||||
# name: Uptime Sensor
|
|
||||||
internal: True
|
|
||||||
id: uptime_sensor
|
|
||||||
update_interval: 1s
|
|
||||||
on_raw_value:
|
|
||||||
then:
|
|
||||||
- text_sensor.template.publish:
|
|
||||||
id: uptime_human
|
|
||||||
state: !lambda |-
|
|
||||||
int seconds = round(id(uptime_sensor).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;
|
|
||||||
return (
|
|
||||||
("Uptime ") +
|
|
||||||
(days ? to_string(days) + "d " : "") +
|
|
||||||
(hours ? to_string(hours) + "h " : "") +
|
|
||||||
(minutes ? to_string(minutes) + "m " : "") +
|
|
||||||
(to_string(seconds) + "s")
|
|
||||||
).c_str();
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- id: update_display
|
- id: update_status
|
||||||
|
then:
|
||||||
|
- lambda: |-
|
||||||
|
// same colors as the other status screens: ok green, waiting orange, details gray
|
||||||
|
static const uint32_t OK = 0x4DD966, WAIT = 0xFFB300;
|
||||||
|
auto set = [](lv_obj_t *label, const std::string &text, uint32_t color) {
|
||||||
|
lv_label_set_text(label, text.c_str());
|
||||||
|
lv_obj_set_style_text_color(label, lv_color_hex(color), LV_PART_MAIN);
|
||||||
|
};
|
||||||
|
|
||||||
|
const bool wifi_ok = wifi::global_wifi_component->is_connected();
|
||||||
|
set(id(status_wifi),
|
||||||
|
wifi_ok ? str_sprintf("WiFi: connected (%d dBm)", wifi::global_wifi_component->wifi_rssi())
|
||||||
|
: std::string("WiFi: connecting..."),
|
||||||
|
wifi_ok ? OK : WAIT);
|
||||||
|
|
||||||
|
// IPv6 first (skip link-local), then IPv4
|
||||||
|
std::string ips;
|
||||||
|
if (wifi_ok) {
|
||||||
|
char buf[network::IP_ADDRESS_BUFFER_SIZE];
|
||||||
|
for (bool v6 : {true, false}) {
|
||||||
|
for (auto &ip : wifi::global_wifi_component->get_ip_addresses()) {
|
||||||
|
if (!ip.is_set() || ip.is_ip6() != v6)
|
||||||
|
continue;
|
||||||
|
ip.str_to(buf);
|
||||||
|
if (v6 && strncmp(buf, "fe80", 4) == 0)
|
||||||
|
continue;
|
||||||
|
if (!ips.empty())
|
||||||
|
ips += "\n";
|
||||||
|
ips += buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lv_label_set_text(id(status_ips), ips.c_str());
|
||||||
|
if (ips.empty())
|
||||||
|
lv_obj_add_flag(id(status_ips), LV_OBJ_FLAG_HIDDEN);
|
||||||
|
else
|
||||||
|
lv_obj_remove_flag(id(status_ips), LV_OBJ_FLAG_HIDDEN);
|
||||||
|
|
||||||
|
const bool api_ok = api::global_api_server->is_connected();
|
||||||
|
set(id(status_ha), api_ok ? "Home Assistant: connected" : "Home Assistant: waiting...",
|
||||||
|
api_ok ? OK : WAIT);
|
||||||
|
|
||||||
|
const uint32_t up = millis() / 1000;
|
||||||
|
lv_label_set_text(id(status_footer),
|
||||||
|
str_sprintf("ESPHome %s · up %ud %02u:%02u:%02u", ESPHOME_VERSION, up / 86400,
|
||||||
|
(up / 3600) % 24, (up / 60) % 60, up % 60).c_str());
|
||||||
|
interval:
|
||||||
|
- interval: 1s
|
||||||
then:
|
then:
|
||||||
# - lvgl.indicator.line.update:
|
|
||||||
# id: power_meter_input
|
|
||||||
# value: !lambda return id(counting_number).state;
|
|
||||||
- lvgl.label.update:
|
- lvgl.label.update:
|
||||||
id: battery_kw
|
id: clock_label
|
||||||
text: !lambda |-
|
text: !lambda |-
|
||||||
static char buf[8];
|
auto now = id(ha_time).now();
|
||||||
snprintf(buf, sizeof(buf), "%.1fkW", id(counting_number).state);
|
if (!now.is_valid())
|
||||||
return buf;
|
return std::string("--:--");
|
||||||
- lvgl.label.update:
|
return now.strftime("%H:%M");
|
||||||
id: battery_status
|
- if:
|
||||||
text_color: 0xFF0000
|
condition:
|
||||||
#text: "charging"
|
lvgl.page.is_showing: status_page
|
||||||
text: "discharging"
|
then:
|
||||||
- lvgl.label.update:
|
- script.execute: update_status
|
||||||
id: battery_soc
|
|
||||||
text: !lambda |-
|
|
||||||
static char buf[8];
|
|
||||||
snprintf(buf, sizeof(buf), "%.1f%%", id(counting_number).state);
|
|
||||||
return buf;
|
|
||||||
- lvgl.label.update:
|
|
||||||
id: solar_kw
|
|
||||||
text: !lambda |-
|
|
||||||
static char buf[8];
|
|
||||||
snprintf(buf, sizeof(buf), "%.1fkW", id(counting_number).state);
|
|
||||||
return buf;
|
|
||||||
# - lvgl.img.update:
|
|
||||||
# id: img_solar_power
|
|
||||||
# src: solar_power_icon
|
|
||||||
# img_recolor: 0xFFF000 #mixes this color with the base image
|
|
||||||
# #img_recolor_opa: 100% #opacity defaults to 0% = must set this for recolor to take effect
|
|
||||||
# #bg_color: 0xFFFFFF
|
|
||||||
# # - lvgl.indicator.line.update:
|
|
||||||
# # id: power_meter_input2
|
|
||||||
# # value: !lambda return id(counting_number).state;
|
|
||||||
- lvgl.label.update:
|
|
||||||
id: battery_kw2
|
|
||||||
text: !lambda |-
|
|
||||||
static char buf[8];
|
|
||||||
snprintf(buf, sizeof(buf), "%.1fkW", id(counting_number).state);
|
|
||||||
return buf;
|
|
||||||
- lvgl.label.update:
|
|
||||||
id: battery_status2
|
|
||||||
text_color: 0xFF0000
|
|
||||||
#text: "charging"
|
|
||||||
text: "discharging"
|
|
||||||
- lvgl.label.update:
|
|
||||||
id: battery_soc2
|
|
||||||
text: !lambda |-
|
|
||||||
static char buf[8];
|
|
||||||
snprintf(buf, sizeof(buf), "%.1f%%", id(counting_number).state);
|
|
||||||
return buf;
|
|
||||||
- lvgl.label.update:
|
|
||||||
id: solar_kw2
|
|
||||||
text: !lambda |-
|
|
||||||
static char buf[8];
|
|
||||||
snprintf(buf, sizeof(buf), "%.1fkW", id(counting_number).state);
|
|
||||||
return buf;
|
|
||||||
# - lvgl.img.update:
|
|
||||||
# id: img_solar_power2
|
|
||||||
# src: solar_power_icon
|
|
||||||
# img_recolor: 0xFFF000 #mixes this color with the base image
|
|
||||||
# #img_recolor_opa: 100% #opacity defaults to 0% = must set this for recolor to take effect
|
|
||||||
# #bg_color: 0xFFFFFF
|
|
||||||
|
|
||||||
image:
|
image:
|
||||||
- file: mdi:sun-wireless-outline
|
- platform: file
|
||||||
#"solar-power-invert.png"
|
file: assets/images/made-for-esphome-white-on-black.svg
|
||||||
# mdi:solar-power
|
id: esphome_logo
|
||||||
# mdi:battery-charging-outline
|
resize: 400x118
|
||||||
# mdi:battery-arrow-up-outline
|
type: RGB565
|
||||||
# mdi:battery-arrow-down-outline
|
transparency: alpha_channel
|
||||||
id: solar_power_icon
|
|
||||||
resize: 50x50
|
|
||||||
type: BINARY
|
|
||||||
|
|
||||||
- file: mdi:battery-arrow-down-outline
|
font:
|
||||||
id: home_battery_icon
|
- file: "gfonts://Roboto@500"
|
||||||
resize: 30x30
|
id: font_clock
|
||||||
type: BINARY
|
size: 200
|
||||||
|
glyphs: "0123456789:-"
|
||||||
|
- file: "gfonts://Roboto"
|
||||||
|
id: font_title
|
||||||
|
size: 40
|
||||||
|
- file: "gfonts://Roboto"
|
||||||
|
id: font_status
|
||||||
|
size: 26
|
||||||
|
glyphs: "·"
|
||||||
|
glyphsets: [GF_Latin_Kernel]
|
||||||
|
|
||||||
lvgl:
|
lvgl:
|
||||||
log_level: INFO
|
log_level: WARN
|
||||||
color_depth: 16
|
color_depth: 16
|
||||||
#buffer_size: 25%
|
bg_color: 0x000000
|
||||||
bg_color: 0
|
text_color: 0xFFFFFF
|
||||||
border_width: 0
|
theme:
|
||||||
outline_width: 0
|
buttonmatrix:
|
||||||
shadow_width: 0
|
|
||||||
text_font: unscii_16
|
|
||||||
align: center
|
|
||||||
style_definitions:
|
|
||||||
- id: meter_style
|
|
||||||
text_font: unscii_8
|
|
||||||
#state_default: null
|
|
||||||
- id: font_style
|
|
||||||
text_font: MONTSERRAT_24
|
|
||||||
#text_font: unscii_16
|
|
||||||
align: center
|
|
||||||
text_color: 0xFFFFFF
|
|
||||||
#bg_opa: cover
|
|
||||||
bg_opa: TRANSP
|
bg_opa: TRANSP
|
||||||
bg_color: 0
|
border_width: 0
|
||||||
radius: 4
|
items:
|
||||||
pad_all: 2
|
bg_color: 0x2B2F36
|
||||||
- id: details_style
|
text_color: 0xFFFFFF
|
||||||
text_font: MONTSERRAT_18
|
radius: 12
|
||||||
align: center
|
checked:
|
||||||
text_color: 0xFFFFFF
|
bg_color: 0xE08A00
|
||||||
#bg_opa: cover
|
top_layer:
|
||||||
bg_opa: TRANSP
|
widgets:
|
||||||
bg_color: 0
|
# same navigation bar as on mcu-lvgl-trainer
|
||||||
radius: 4
|
- buttonmatrix:
|
||||||
pad_all: 2
|
id: nav_bar
|
||||||
widgets:
|
align: bottom_mid
|
||||||
- obj: # Meter
|
width: 100%
|
||||||
height: 240
|
height: 64
|
||||||
width: 240
|
pad_all: 4
|
||||||
#bg_opa: TRANSP
|
pad_column: 6
|
||||||
bg_color: 0
|
items:
|
||||||
border_width: 0
|
text_font: montserrat_28
|
||||||
outline_width: 0
|
rows:
|
||||||
shadow_width: 0
|
- buttons:
|
||||||
pad_all: 4
|
- id: page_prev
|
||||||
align: TOP_LEFT
|
text: "\uF053"
|
||||||
#scrollbar_mode: "off"
|
on_press:
|
||||||
widgets:
|
then:
|
||||||
- meter: # Gradient color arc
|
lvgl.page.previous:
|
||||||
height: 100%
|
- id: page_home
|
||||||
width: 100%
|
text: "\uF015"
|
||||||
border_width: 0
|
on_press:
|
||||||
outline_width: 0
|
then:
|
||||||
align: center
|
lvgl.page.show: main_page
|
||||||
bg_color: 0
|
- id: page_next
|
||||||
styles: meter_style
|
text: "\uF054"
|
||||||
#bg_opa: TRANSP
|
on_press:
|
||||||
scales:
|
then:
|
||||||
angle_range: 180 # sets the total angle to 180 = starts mid left and ends mid right
|
lvgl.page.next:
|
||||||
range_to: 10
|
pages:
|
||||||
range_from: -10
|
# 1: clock
|
||||||
ticks:
|
- id: main_page
|
||||||
count: 0
|
widgets:
|
||||||
indicators:
|
- label:
|
||||||
- line:
|
id: clock_label
|
||||||
id: power_meter_input
|
align: CENTER
|
||||||
width: 8
|
y: -32
|
||||||
color: 0xFFFFFF
|
text_font: font_clock
|
||||||
r_mod: 12 #sets line length by this much difference from the scale default radius
|
text: "--:--"
|
||||||
value: 50
|
|
||||||
# - image:
|
# 2: status (shown until Home Assistant connects)
|
||||||
# id: power_meter_input_img
|
- id: status_page
|
||||||
- arc:
|
on_load:
|
||||||
color: 0xFF3000
|
- script.execute: update_status
|
||||||
r_mod: 10 #radius difference from the scale default radius
|
widgets:
|
||||||
width: 20
|
- image:
|
||||||
start_value: -10
|
src: esphome_logo
|
||||||
end_value: 0
|
align: TOP_MID
|
||||||
- arc:
|
y: 16
|
||||||
color: 0x00FF00
|
- label:
|
||||||
r_mod: 10 #radius difference from the scale default radius
|
align: TOP_MID
|
||||||
width: 20
|
y: 140
|
||||||
start_value: 0
|
text_font: font_title
|
||||||
end_value: 10
|
text: "${name}"
|
||||||
#- canvas:
|
- obj:
|
||||||
- arc: # black arc to erase middle part of meter indicator line
|
id: status_lines
|
||||||
height: 160
|
x: 60
|
||||||
width: 160
|
y: 196
|
||||||
align: center
|
width: 700
|
||||||
arc_color: 0x000000 # background color
|
height: SIZE_CONTENT
|
||||||
arc_width: 150
|
bg_opa: TRANSP
|
||||||
start_angle: 0
|
border_width: 0
|
||||||
end_angle: 360
|
pad_all: 0
|
||||||
indicator:
|
scrollbar_mode: "OFF"
|
||||||
arc_width: 150
|
layout:
|
||||||
arc_color: 0x000000
|
type: flex
|
||||||
- label: # gauge lower and higher range indicators
|
flex_flow: column
|
||||||
styles: font_style
|
pad_row: 8
|
||||||
text_font: MONTSERRAT_18 # override font size
|
widgets:
|
||||||
y: 8 #negative = higher
|
- label:
|
||||||
x: -99
|
id: status_wifi
|
||||||
text: "-10"
|
text_font: font_status
|
||||||
- label:
|
text: ""
|
||||||
styles: font_style
|
- label:
|
||||||
text_font: MONTSERRAT_18 # override font size
|
id: status_ips
|
||||||
y: 8 #negative = higher
|
text_font: font_status
|
||||||
x: 99
|
text_color: 0x8C8C8C
|
||||||
text: "+10"
|
pad_left: 30
|
||||||
- label:
|
text: ""
|
||||||
styles: font_style
|
- label:
|
||||||
id: battery_status
|
id: status_ha
|
||||||
y: -35 #negative = higher
|
text_font: font_status
|
||||||
- label:
|
text: ""
|
||||||
styles: font_style
|
- label:
|
||||||
id: battery_kw
|
id: status_footer
|
||||||
y: -60
|
text_font: font_status
|
||||||
- label:
|
text_color: 0x8C8C8C
|
||||||
styles: font_style
|
text: ""
|
||||||
text_font: MONTSERRAT_40 # override font size
|
|
||||||
id: battery_soc
|
|
||||||
y: 0
|
|
||||||
- label:
|
|
||||||
styles: font_style
|
|
||||||
id: solar_kw
|
|
||||||
text_color: 0xFFFF00
|
|
||||||
y: 90
|
|
||||||
#- img:
|
|
||||||
# src: home_battery_icon
|
|
||||||
#id: img_solar_power
|
|
||||||
#align: center
|
|
||||||
#img_recolor: 0xFFFFFF
|
|
||||||
#img_recolor_opa: 100% #opacity defaults to 0% = must set this for recolor to take effect
|
|
||||||
#bg_color: 0xFFFFFF
|
|
||||||
#y: -35
|
|
||||||
#x: -60
|
|
||||||
- image:
|
|
||||||
src: solar_power_icon
|
|
||||||
id: img_solar_power
|
|
||||||
align: center
|
|
||||||
image_recolor: 0xFFFF00
|
|
||||||
image_recolor_opa: 100% #opacity defaults to 0% = must set this for recolor to take effect
|
|
||||||
#bg_color: 0xFFFFFF
|
|
||||||
y: 50
|
|
||||||
#x: -60
|
|
||||||
- obj: # Meter
|
|
||||||
height: 240
|
|
||||||
width: 240
|
|
||||||
#bg_opa: TRANSP
|
|
||||||
bg_color: 0
|
|
||||||
border_width: 0
|
|
||||||
outline_width: 0
|
|
||||||
shadow_width: 0
|
|
||||||
pad_all: 4
|
|
||||||
align: TOP_MID
|
|
||||||
#scrollbar_mode: "off"
|
|
||||||
widgets:
|
|
||||||
- meter: # Gradient color arc
|
|
||||||
height: 100%
|
|
||||||
width: 100%
|
|
||||||
border_width: 0
|
|
||||||
outline_width: 0
|
|
||||||
align: center
|
|
||||||
bg_color: 0
|
|
||||||
styles: meter_style
|
|
||||||
#bg_opa: TRANSP
|
|
||||||
scales:
|
|
||||||
angle_range: 180 # sets the total angle to 180 = starts mid left and ends mid right
|
|
||||||
range_to: 10
|
|
||||||
range_from: -10
|
|
||||||
ticks:
|
|
||||||
count: 0
|
|
||||||
indicators:
|
|
||||||
- line:
|
|
||||||
id: power_meter_input2
|
|
||||||
width: 8
|
|
||||||
color: 0xFFFFFF
|
|
||||||
r_mod: 12 #sets line length by this much difference from the scale default radius
|
|
||||||
value: 50
|
|
||||||
# - image:
|
|
||||||
# id: power_meter_input_img2
|
|
||||||
- arc:
|
|
||||||
color: 0xFF3000
|
|
||||||
r_mod: 10 #radius difference from the scale default radius
|
|
||||||
width: 20
|
|
||||||
start_value: -10
|
|
||||||
end_value: 0
|
|
||||||
- arc:
|
|
||||||
color: 0x00FF00
|
|
||||||
r_mod: 10 #radius difference from the scale default radius
|
|
||||||
width: 20
|
|
||||||
start_value: 0
|
|
||||||
end_value: 10
|
|
||||||
#- canvas:
|
|
||||||
- arc: # black arc to erase middle part of meter indicator line
|
|
||||||
height: 160
|
|
||||||
width: 160
|
|
||||||
align: center
|
|
||||||
arc_color: 0x000000 # background color
|
|
||||||
arc_width: 150
|
|
||||||
start_angle: 0
|
|
||||||
end_angle: 360
|
|
||||||
indicator:
|
|
||||||
arc_width: 150
|
|
||||||
arc_color: 0x000000
|
|
||||||
- label: # gauge lower and higher range indicators
|
|
||||||
styles: font_style
|
|
||||||
text_font: MONTSERRAT_18 # override font size
|
|
||||||
y: 8 #negative = higher
|
|
||||||
x: -99
|
|
||||||
text: "-10"
|
|
||||||
- label:
|
|
||||||
styles: font_style
|
|
||||||
text_font: MONTSERRAT_18 # override font size
|
|
||||||
y: 8 #negative = higher
|
|
||||||
x: 99
|
|
||||||
text: "+10"
|
|
||||||
- label:
|
|
||||||
styles: font_style
|
|
||||||
id: battery_status2
|
|
||||||
y: -35 #negative = higher
|
|
||||||
- label:
|
|
||||||
styles: font_style
|
|
||||||
id: battery_kw2
|
|
||||||
y: -60
|
|
||||||
- label:
|
|
||||||
styles: font_style
|
|
||||||
text_font: MONTSERRAT_40 # override font size
|
|
||||||
id: battery_soc2
|
|
||||||
y: 0
|
|
||||||
- label:
|
|
||||||
styles: font_style
|
|
||||||
id: solar_kw2
|
|
||||||
text_color: 0xFFFF00
|
|
||||||
y: 90
|
|
||||||
#- img:
|
|
||||||
# src: home_battery_icon
|
|
||||||
#id: img_solar_power
|
|
||||||
#align: center
|
|
||||||
#img_recolor: 0xFFFFFF
|
|
||||||
#img_recolor_opa: 100% #opacity defaults to 0% = must set this for recolor to take effect
|
|
||||||
#bg_color: 0xFFFFFF
|
|
||||||
#y: -35
|
|
||||||
#x: -60
|
|
||||||
- image:
|
|
||||||
src: solar_power_icon
|
|
||||||
id: img_solar_power2
|
|
||||||
align: center
|
|
||||||
image_recolor: 0xFFFF00
|
|
||||||
image_recolor_opa: 100% #opacity defaults to 0% = must set this for recolor to take effect
|
|
||||||
#bg_color: 0xFFFFFF
|
|
||||||
y: 50
|
|
||||||
#x: -60
|
|
||||||
- obj: # Meter
|
|
||||||
height: 240
|
|
||||||
width: 240
|
|
||||||
#bg_opa: TRANSP
|
|
||||||
bg_color: 0
|
|
||||||
border_width: 0
|
|
||||||
outline_width: 0
|
|
||||||
shadow_width: 0
|
|
||||||
pad_all: 4
|
|
||||||
align: BOTTOM_LEFT
|
|
||||||
#scrollbar_mode: "off"
|
|
||||||
widgets:
|
|
||||||
- meter: # Gradient color arc
|
|
||||||
height: 100%
|
|
||||||
width: 100%
|
|
||||||
border_width: 0
|
|
||||||
outline_width: 0
|
|
||||||
align: center
|
|
||||||
bg_color: 0
|
|
||||||
styles: meter_style
|
|
||||||
#bg_opa: TRANSP
|
|
||||||
scales:
|
|
||||||
angle_range: 180 # sets the total angle to 180 = starts mid left and ends mid right
|
|
||||||
range_to: 10
|
|
||||||
range_from: -10
|
|
||||||
ticks:
|
|
||||||
count: 0
|
|
||||||
indicators:
|
|
||||||
- line:
|
|
||||||
id: power_meter_input3
|
|
||||||
width: 8
|
|
||||||
color: 0xFFFFFF
|
|
||||||
r_mod: 12 #sets line length by this much difference from the scale default radius
|
|
||||||
value: 50
|
|
||||||
# - image:
|
|
||||||
# id: power_meter_input_img3
|
|
||||||
- arc:
|
|
||||||
color: 0xFF3000
|
|
||||||
r_mod: 10 #radius difference from the scale default radius
|
|
||||||
width: 20
|
|
||||||
start_value: -10
|
|
||||||
end_value: 0
|
|
||||||
- arc:
|
|
||||||
color: 0x00FF00
|
|
||||||
r_mod: 10 #radius difference from the scale default radius
|
|
||||||
width: 20
|
|
||||||
start_value: 0
|
|
||||||
end_value: 10
|
|
||||||
#- canvas:
|
|
||||||
- arc: # black arc to erase middle part of meter indicator line
|
|
||||||
height: 160
|
|
||||||
width: 160
|
|
||||||
align: center
|
|
||||||
arc_color: 0x000000 # background color
|
|
||||||
arc_width: 150
|
|
||||||
start_angle: 0
|
|
||||||
end_angle: 360
|
|
||||||
indicator:
|
|
||||||
arc_width: 150
|
|
||||||
arc_color: 0x000000
|
|
||||||
- label: # gauge lower and higher range indicators
|
|
||||||
styles: font_style
|
|
||||||
text_font: MONTSERRAT_18 # override font size
|
|
||||||
y: 8 #negative = higher
|
|
||||||
x: -99
|
|
||||||
text: "-10"
|
|
||||||
- label:
|
|
||||||
styles: font_style
|
|
||||||
text_font: MONTSERRAT_18 # override font size
|
|
||||||
y: 8 #negative = higher
|
|
||||||
x: 99
|
|
||||||
text: "+10"
|
|
||||||
- label:
|
|
||||||
styles: font_style
|
|
||||||
id: battery_status3
|
|
||||||
y: -35 #negative = higher
|
|
||||||
- label:
|
|
||||||
styles: font_style
|
|
||||||
id: battery_kw3
|
|
||||||
y: -60
|
|
||||||
- label:
|
|
||||||
styles: font_style
|
|
||||||
text_font: MONTSERRAT_40 # override font size
|
|
||||||
id: battery_soc3
|
|
||||||
y: 0
|
|
||||||
- label:
|
|
||||||
styles: font_style
|
|
||||||
id: solar_kw3
|
|
||||||
text_color: 0xFFFF00
|
|
||||||
y: 90
|
|
||||||
#- img:
|
|
||||||
# src: home_battery_icon
|
|
||||||
#id: img_solar_power
|
|
||||||
#align: center
|
|
||||||
#img_recolor: 0xFFFFFF
|
|
||||||
#img_recolor_opa: 100% #opacity defaults to 0% = must set this for recolor to take effect
|
|
||||||
#bg_color: 0xFFFFFF
|
|
||||||
#y: -35
|
|
||||||
#x: -60
|
|
||||||
- image:
|
|
||||||
src: solar_power_icon
|
|
||||||
id: img_solar_power3
|
|
||||||
align: center
|
|
||||||
image_recolor: 0xFFFF00
|
|
||||||
image_recolor_opa: 100% #opacity defaults to 0% = must set this for recolor to take effect
|
|
||||||
#bg_color: 0xFFFFFF
|
|
||||||
y: 50
|
|
||||||
#x: -60
|
|
||||||
+579
-77
@@ -4,94 +4,380 @@ substitutions:
|
|||||||
name: M5Paper
|
name: M5Paper
|
||||||
area: DEV
|
area: DEV
|
||||||
comment: M5Paper
|
comment: M5Paper
|
||||||
|
waste_calendar: calendar.https_www_hviezdoslavov_sk_kalendar_icalfeed_category_0_ongoing_1
|
||||||
|
# contacts + manual
|
||||||
|
birthday_calendars: calendar.birthdays, calendar.narodeniny
|
||||||
|
|
||||||
|
# Power modes
|
||||||
|
# - USB: always on, WiFi + API, events refreshed and redrawn after midnight
|
||||||
|
# - battery: RTC wakes the board at 00:05, it refreshes the events, draws them and powers
|
||||||
|
# itself off; the RTC timer lasts at most 255 min, so the wake-ups on the way to
|
||||||
|
# midnight only re-arm it
|
||||||
|
# The power button (and USB) also connects to WiFi: the events are refreshed on every
|
||||||
|
# Home Assistant connection outside the battery cycle.
|
||||||
|
# Screens: status (after power-on, until Home Assistant connects) and calendar
|
||||||
|
# The mode is detected by trying to power off: on USB the board keeps running.
|
||||||
|
|
||||||
esphome:
|
esphome:
|
||||||
name: mcu-${device}
|
name: mcu-${device}
|
||||||
friendly_name: ${name}
|
friendly_name: ${name}
|
||||||
area: ${area}
|
area: ${area}
|
||||||
comment: ${comment}
|
comment: ${comment}
|
||||||
|
includes:
|
||||||
|
- include/m5paper_calendar.h
|
||||||
|
on_boot:
|
||||||
|
- priority: 600
|
||||||
|
then:
|
||||||
|
- lambda: |-
|
||||||
|
// BM8563 CONTROL_STATUS_2: TF (bit 2) is set when the timer woke us up
|
||||||
|
const uint8_t reg = 0x01;
|
||||||
|
uint8_t status = 0;
|
||||||
|
if (id(i2c_main)->write_readv(0x51, ®, 1, &status, 1) == i2c::ERROR_OK) {
|
||||||
|
id(rtc_wake) = status & 0x04;
|
||||||
|
// clear TF and TIE so the RTC releases the power latch
|
||||||
|
const uint8_t clear[2] = {reg, uint8_t(status & ~0x05)};
|
||||||
|
id(i2c_main)->write(0x51, clear, 2);
|
||||||
|
}
|
||||||
|
m5paper_calendar::load(m5paper_calendar::events());
|
||||||
|
ESP_LOGI("m5paper", "Wake-up: %s, events: %u", id(rtc_wake) ? "RTC" : "power-on", m5paper_calendar::events().count);
|
||||||
|
- priority: -100
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return id(rtc_wake);
|
||||||
|
then:
|
||||||
|
- script.execute: battery_cycle
|
||||||
|
else:
|
||||||
|
# power button / USB: stay awake for a while (logs, OTA), then decide the mode
|
||||||
|
- wifi.enable:
|
||||||
|
# IT8951 init runs asynchronously after setup
|
||||||
|
- delay: 3s
|
||||||
|
- component.update: m5paper_display
|
||||||
|
- delay: 2min
|
||||||
|
- script.execute: battery_cycle
|
||||||
|
|
||||||
esp32:
|
esp32:
|
||||||
variant: esp32
|
variant: esp32
|
||||||
toolchain: esp-idf
|
toolchain: esp-idf
|
||||||
framework:
|
framework:
|
||||||
type: esp-idf
|
type: esp-idf
|
||||||
|
advanced:
|
||||||
|
flash_chip: generic
|
||||||
|
minimum_chip_revision: 3.0
|
||||||
|
sram1_as_iram: true
|
||||||
|
|
||||||
psram:
|
psram:
|
||||||
|
|
||||||
external_components:
|
|
||||||
- source: github://Passific/m5paper_esphome
|
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
ipv6_only: !include common/ipv6_only.yaml
|
||||||
|
|
||||||
time:
|
globals:
|
||||||
- platform: homeassistant
|
- id: show_status
|
||||||
id: ha_time
|
type: bool
|
||||||
timezone: Europe/Bratislava
|
initial_value: 'true'
|
||||||
on_time_sync:
|
- id: usb_mode
|
||||||
- bm8563.write_time
|
type: bool
|
||||||
- platform: bm8563
|
initial_value: 'false'
|
||||||
id: rtc_time
|
- id: rtc_wake
|
||||||
sleep_duration: 3600000ms
|
type: bool
|
||||||
|
initial_value: 'false'
|
||||||
|
# battery cycle running: calendar screen regardless of the connection state
|
||||||
|
- id: calendar_page
|
||||||
|
type: bool
|
||||||
|
initial_value: 'false'
|
||||||
|
- id: fetch_done
|
||||||
|
type: bool
|
||||||
|
initial_value: 'false'
|
||||||
|
- id: sleep_s
|
||||||
|
type: int
|
||||||
|
initial_value: '900'
|
||||||
|
|
||||||
m5paper:
|
wifi:
|
||||||
battery_power_pin: GPIO5
|
enable_on_boot: false
|
||||||
main_power_pin: GPIO2
|
on_connect:
|
||||||
# update_interval: 10s
|
- if:
|
||||||
# battery_voltage:
|
condition:
|
||||||
# name: "${name} Battery"
|
lambda: return id(show_status) && !id(calendar_page);
|
||||||
|
then:
|
||||||
|
- component.update: m5paper_display
|
||||||
|
|
||||||
|
api:
|
||||||
|
# status screen until Home Assistant connects (log viewers don't subscribe to states)
|
||||||
|
on_client_connected:
|
||||||
|
- delay: 2s
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
api.connected:
|
||||||
|
state_subscription_only: true
|
||||||
|
then:
|
||||||
|
- lambda: id(show_status) = false;
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return !id(calendar_page);
|
||||||
|
then:
|
||||||
|
- component.update: m5paper_display
|
||||||
|
# outside the battery cycle (power button, USB) WiFi is up anyway: always refresh
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
and:
|
||||||
|
- lambda: return !id(calendar_page);
|
||||||
|
- api.connected:
|
||||||
|
state_subscription_only: true
|
||||||
|
then:
|
||||||
|
- script.execute: fetch_events
|
||||||
|
on_client_disconnected:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
not:
|
||||||
|
api.connected:
|
||||||
|
state_subscription_only: true
|
||||||
|
then:
|
||||||
|
- lambda: id(show_status) = true;
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return !id(calendar_page);
|
||||||
|
then:
|
||||||
|
- component.update: m5paper_display
|
||||||
|
|
||||||
|
# I2C devices (i2c_main)
|
||||||
# 0x44 SHT30
|
# 0x44 SHT30
|
||||||
# 0x50
|
# 0x50 FM24C02 EEPROM (unused)
|
||||||
# 0x51
|
# 0x51 BM8563 RTC
|
||||||
# 0x5D GT911
|
# 0x5D GT911 touch (docs say 0x14; the driver tries 0x5D, then 0x14)
|
||||||
|
|
||||||
i2c:
|
i2c:
|
||||||
- id: i2c_main
|
- id: i2c_main
|
||||||
sda: 21
|
sda: 21
|
||||||
scl: 22
|
scl: 22
|
||||||
scan: true #false
|
scan: true
|
||||||
|
# Port A
|
||||||
# - id: i2c_a
|
# - id: i2c_a
|
||||||
# sda: 25
|
# sda: 25
|
||||||
# scl: 32
|
# scl: 32
|
||||||
# scan: true #false
|
# Port B
|
||||||
# - id: i2c_b
|
# - id: i2c_b
|
||||||
# sda: 26
|
# sda: 26
|
||||||
# scl: 33
|
# scl: 33
|
||||||
# scan: true #false
|
# Port C
|
||||||
# - id: i2c_c
|
# - id: i2c_c
|
||||||
# sda: 18
|
# sda: 18
|
||||||
# scl: 19
|
# scl: 19
|
||||||
# scan: true #false
|
|
||||||
|
|
||||||
spi:
|
spi:
|
||||||
clk_pin: 14
|
clk_pin: 14
|
||||||
miso_pin: 13
|
miso_pin: 13
|
||||||
mosi_pin: 12
|
mosi_pin: 12
|
||||||
|
|
||||||
|
switch:
|
||||||
|
# Power control MOS (no PMU on M5Paper); on battery the device powers off when this goes low
|
||||||
|
- platform: gpio
|
||||||
|
id: main_power
|
||||||
|
pin:
|
||||||
|
number: GPIO2
|
||||||
|
ignore_strapping_warning: true
|
||||||
|
restore_mode: ALWAYS_ON
|
||||||
|
internal: true
|
||||||
|
# External power (Grove ports)
|
||||||
|
- platform: gpio
|
||||||
|
id: ext_power
|
||||||
|
pin:
|
||||||
|
number: GPIO5
|
||||||
|
ignore_strapping_warning: true
|
||||||
|
restore_mode: ALWAYS_ON
|
||||||
|
internal: true
|
||||||
|
|
||||||
|
script:
|
||||||
|
# One wake-up on battery: refresh the events if they are old, draw them, power off
|
||||||
|
- id: battery_cycle
|
||||||
|
mode: single
|
||||||
|
then:
|
||||||
|
- lambda: id(calendar_page) = true;
|
||||||
|
- wait_until:
|
||||||
|
condition:
|
||||||
|
lambda: return id(rtc_time).now().is_valid();
|
||||||
|
timeout: 10s
|
||||||
|
- if:
|
||||||
|
# RTC wake-up on the way to midnight: nothing to draw
|
||||||
|
condition:
|
||||||
|
lambda: |-
|
||||||
|
const auto now = id(rtc_time).now();
|
||||||
|
return id(rtc_wake) && now.is_valid() && now.hour != 0;
|
||||||
|
then:
|
||||||
|
- script.execute: try_sleep
|
||||||
|
else:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return m5paper_calendar::stale(id(rtc_time).now());
|
||||||
|
then:
|
||||||
|
- wifi.enable:
|
||||||
|
- wait_until:
|
||||||
|
condition:
|
||||||
|
api.connected:
|
||||||
|
state_subscription_only: true
|
||||||
|
timeout: 45s
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
api.connected:
|
||||||
|
state_subscription_only: true
|
||||||
|
then:
|
||||||
|
- script.execute: fetch_events
|
||||||
|
- wait_until:
|
||||||
|
condition:
|
||||||
|
lambda: return id(fetch_done);
|
||||||
|
timeout: 15s
|
||||||
|
- component.update: m5paper_display
|
||||||
|
# let the e-paper refresh finish before cutting power
|
||||||
|
- delay: 4s
|
||||||
|
- script.execute: try_sleep
|
||||||
|
|
||||||
|
# today and the next 31 days (the month grid and the list)
|
||||||
|
- id: fetch_events
|
||||||
|
mode: single
|
||||||
|
then:
|
||||||
|
- lambda: id(fetch_done) = false;
|
||||||
|
- homeassistant.action:
|
||||||
|
action: calendar.get_events
|
||||||
|
data:
|
||||||
|
entity_id: ${waste_calendar}, ${birthday_calendars}
|
||||||
|
start_date_time: !lambda return id(rtc_time).now().strftime("%Y-%m-%d 00:00:00");
|
||||||
|
duration: "744:00:00"
|
||||||
|
capture_response: true
|
||||||
|
on_success:
|
||||||
|
- lambda: |-
|
||||||
|
auto &ev = m5paper_calendar::events();
|
||||||
|
if (m5paper_calendar::parse(response, "${waste_calendar}", id(rtc_time).now().timestamp, ev)) {
|
||||||
|
const bool saved = m5paper_calendar::save(ev);
|
||||||
|
ESP_LOGI("m5paper", "Events: %u%s", ev.count, saved ? "" : " (flash write failed)");
|
||||||
|
} else {
|
||||||
|
ESP_LOGW("m5paper", "Events: unexpected response");
|
||||||
|
}
|
||||||
|
id(fetch_done) = true;
|
||||||
|
# the battery cycle draws on its own
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return !id(calendar_page);
|
||||||
|
then:
|
||||||
|
- component.update: m5paper_display
|
||||||
|
on_error:
|
||||||
|
- lambda: |-
|
||||||
|
ESP_LOGW("m5paper", "Events: %s", error.c_str());
|
||||||
|
id(fetch_done) = true;
|
||||||
|
|
||||||
|
# Arm the RTC and cut power; if we are still running afterwards, we are on USB
|
||||||
|
- id: try_sleep
|
||||||
|
mode: single
|
||||||
|
then:
|
||||||
|
- lambda: |-
|
||||||
|
// next 00:05; the timer lasts at most 255 min, so get there in steps
|
||||||
|
const int MAX = 255 * 60;
|
||||||
|
int duration = MAX;
|
||||||
|
const auto now = id(rtc_time).now();
|
||||||
|
if (now.is_valid()) {
|
||||||
|
const int sod = now.hour * 3600 + now.minute * 60 + now.second;
|
||||||
|
int left = (86400 + 300 - sod) % 86400;
|
||||||
|
if (left < 600) // just drawn: the 1/60 Hz RTC timer may wake us up to a minute early
|
||||||
|
left += 86400;
|
||||||
|
// leave at least 20 min for the last step, so it cannot fall into the case above
|
||||||
|
duration = left > MAX ? std::min(MAX, left - 1200) : left;
|
||||||
|
}
|
||||||
|
if (duration > 255) // longer timers count whole minutes
|
||||||
|
duration = (duration + 59) / 60 * 60;
|
||||||
|
id(sleep_s) = std::min(duration, MAX);
|
||||||
|
ESP_LOGI("m5paper", "Powering off for %d s", id(sleep_s));
|
||||||
|
- bm8563.start_timer:
|
||||||
|
duration: !lambda return id(sleep_s);
|
||||||
|
# a battery wake-up is shorter than boot_is_good_after: confirm the OTA image
|
||||||
|
# before cutting power, otherwise the bootloader rolls back
|
||||||
|
- safe_mode.mark_successful:
|
||||||
|
- delay: 200ms
|
||||||
|
- switch.turn_off: main_power
|
||||||
|
- delay: 2s
|
||||||
|
# still alive -> USB power
|
||||||
|
- switch.turn_on: main_power
|
||||||
|
- lambda: |-
|
||||||
|
ESP_LOGI("m5paper", "Still running, USB power");
|
||||||
|
id(usb_mode) = true;
|
||||||
|
id(rtc_wake) = false;
|
||||||
|
id(calendar_page) = false;
|
||||||
|
id(show_status) = !api::global_api_server->is_connected_with_state_subscription();
|
||||||
|
- wifi.enable:
|
||||||
|
- component.update: m5paper_display
|
||||||
|
|
||||||
|
interval:
|
||||||
|
# USB mode: the battery voltage drops once USB is unplugged -> go to battery mode
|
||||||
|
- interval: 5min
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: |-
|
||||||
|
static float last = NAN;
|
||||||
|
const float v = id(battery_voltage).state;
|
||||||
|
const bool dropped = id(usb_mode) && !std::isnan(last) && last - v > 0.03f;
|
||||||
|
last = v;
|
||||||
|
return dropped;
|
||||||
|
then:
|
||||||
|
- lambda: |-
|
||||||
|
ESP_LOGI("m5paper", "Battery voltage dropping, checking USB");
|
||||||
|
id(usb_mode) = false;
|
||||||
|
- script.execute: battery_cycle
|
||||||
|
|
||||||
|
time:
|
||||||
|
- id: !extend ha_time
|
||||||
|
on_time_sync:
|
||||||
|
- bm8563.write_time
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return !id(calendar_page);
|
||||||
|
then:
|
||||||
|
- component.update: m5paper_display
|
||||||
|
on_time:
|
||||||
|
# new day
|
||||||
|
- seconds: 0
|
||||||
|
minutes: 0
|
||||||
|
hours: 0
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return !id(show_status) && !id(calendar_page);
|
||||||
|
then:
|
||||||
|
- component.update: m5paper_display
|
||||||
|
# USB mode: keep the stored events fresh for battery mode
|
||||||
|
- seconds: 0
|
||||||
|
minutes: 5
|
||||||
|
hours: 0
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
and:
|
||||||
|
- lambda: return id(usb_mode);
|
||||||
|
- api.connected:
|
||||||
|
state_subscription_only: true
|
||||||
|
then:
|
||||||
|
- script.execute: fetch_events
|
||||||
|
- platform: bm8563
|
||||||
|
id: rtc_time
|
||||||
|
|
||||||
binary_sensor:
|
binary_sensor:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
name: ${name} Button UP
|
name: ${name} Button UP
|
||||||
id: button_up
|
id: button_up
|
||||||
pin:
|
pin:
|
||||||
number: 37
|
number: GPIO37
|
||||||
inverted: true
|
inverted: true
|
||||||
on_press:
|
on_press:
|
||||||
- component.update: m5paper_display
|
- component.update: m5paper_display
|
||||||
on_release:
|
|
||||||
- component.update: m5paper_display
|
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
name: ${name} Button Press
|
name: ${name} Button Press
|
||||||
id: button_press
|
id: button_press
|
||||||
pin:
|
pin:
|
||||||
number: 38
|
number: GPIO38
|
||||||
inverted: true
|
inverted: true
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
name: ${name} Button Down
|
name: ${name} Button Down
|
||||||
id: button_down
|
id: button_down
|
||||||
pin:
|
pin:
|
||||||
number: 39
|
number: GPIO39
|
||||||
inverted: true
|
inverted: true
|
||||||
- platform: touchscreen
|
- platform: touchscreen
|
||||||
name: ${name} Top Left Touch Button
|
name: ${name} Top Left Touch Button
|
||||||
@@ -103,51 +389,19 @@ binary_sensor:
|
|||||||
y_min: 0
|
y_min: 0
|
||||||
y_max: 100
|
y_max: 100
|
||||||
on_press:
|
on_press:
|
||||||
# Example of applying the sleep duration and shutting down to the lowest power mode
|
- script.execute: battery_cycle
|
||||||
- bm8563.apply_sleep_duration
|
|
||||||
- delay: 1s
|
|
||||||
- m5paper.shutdown_main_power
|
|
||||||
|
|
||||||
font:
|
button:
|
||||||
- file: "gfonts://Roboto"
|
- platform: template
|
||||||
id: font_default
|
name: Refresh Display
|
||||||
size: 20
|
entity_category: config
|
||||||
- file: "gfonts://Roboto"
|
on_press:
|
||||||
id: roboto_32
|
- component.update: m5paper_display
|
||||||
size: 32
|
- platform: template
|
||||||
- file: "gfonts://Roboto"
|
name: Refresh Calendar
|
||||||
id: roboto_24
|
entity_category: config
|
||||||
size: 24
|
on_press:
|
||||||
- file: "gfonts://Roboto"
|
- script.execute: fetch_events
|
||||||
id: roboto_12
|
|
||||||
size: 12
|
|
||||||
|
|
||||||
display:
|
|
||||||
- platform: it8951e
|
|
||||||
id: m5paper_display
|
|
||||||
cs_pin: GPIO15
|
|
||||||
reset_pin: GPIO23
|
|
||||||
busy_pin: GPIO27
|
|
||||||
rotation: 90
|
|
||||||
reversed: False
|
|
||||||
update_interval: "never"
|
|
||||||
lambda: |-
|
|
||||||
it.printf(25, 25, id(roboto_32), "%.1f°", id(current_temperature).state);
|
|
||||||
it.strftime(350, 25, id(roboto_32), "%H:%M:%S", id(rtc_time).now());
|
|
||||||
|
|
||||||
touchscreen:
|
|
||||||
- platform: gt911
|
|
||||||
display: m5paper_display
|
|
||||||
id: gt911_touchscreen
|
|
||||||
interrupt_pin: GPIO36
|
|
||||||
|
|
||||||
# display:
|
|
||||||
# - platform: waveshare_epaper
|
|
||||||
# cs_pin: 15
|
|
||||||
# model: 2.90in
|
|
||||||
# full_update_every: 30
|
|
||||||
# lambda: |-
|
|
||||||
# it.print(0, 0, id(font1), "Hello World!");
|
|
||||||
|
|
||||||
sensor:
|
sensor:
|
||||||
- platform: sht3xd
|
- platform: sht3xd
|
||||||
@@ -156,9 +410,257 @@ sensor:
|
|||||||
id: current_temperature
|
id: current_temperature
|
||||||
humidity:
|
humidity:
|
||||||
name: Humidity
|
name: Humidity
|
||||||
|
id: current_humidity
|
||||||
address: 0x44
|
address: 0x44
|
||||||
update_interval: 60s
|
update_interval: 60s
|
||||||
|
- platform: adc
|
||||||
|
id: battery_voltage
|
||||||
|
name: Battery Voltage
|
||||||
|
pin: GPIO35
|
||||||
|
attenuation: 12db
|
||||||
|
samples: 16
|
||||||
|
update_interval: 60s
|
||||||
|
entity_category: diagnostic
|
||||||
|
filters:
|
||||||
|
- multiply: 2.0 # 1:2 voltage divider
|
||||||
|
- platform: copy
|
||||||
|
source_id: battery_voltage
|
||||||
|
id: battery_level
|
||||||
|
name: Battery
|
||||||
|
unit_of_measurement: "%"
|
||||||
|
device_class: battery
|
||||||
|
accuracy_decimals: 0
|
||||||
|
filters:
|
||||||
|
- calibrate_linear:
|
||||||
|
- 3.3 -> 0.0
|
||||||
|
- 4.15 -> 100.0
|
||||||
|
- clamp:
|
||||||
|
min_value: 0
|
||||||
|
max_value: 100
|
||||||
|
|
||||||
# touchscreen:
|
.image_defaults:
|
||||||
# - platform: gt911
|
icon: &icon {platform: file, type: BINARY, transparency: chroma_key}
|
||||||
# interrupt_pin: 36
|
|
||||||
|
image:
|
||||||
|
- platform: file
|
||||||
|
file: assets/images/made-for-esphome-white-on-black.svg
|
||||||
|
id: esphome_logo
|
||||||
|
resize: 460x136
|
||||||
|
type: GRAYSCALE
|
||||||
|
transparency: alpha_channel
|
||||||
|
# same order as m5paper_calendar::Kind
|
||||||
|
- {<<: *icon, file: "mdi:cake-variant", id: icon_birthday, resize: 32x32}
|
||||||
|
- {<<: *icon, file: "mdi:trash-can", id: icon_waste, resize: 32x32}
|
||||||
|
- {<<: *icon, file: "mdi:cake-variant", id: mark_birthday, resize: 18x18}
|
||||||
|
- {<<: *icon, file: "mdi:trash-can", id: mark_waste, resize: 18x18}
|
||||||
|
|
||||||
|
font:
|
||||||
|
- file: "gfonts://Roboto"
|
||||||
|
id: font_title
|
||||||
|
size: 48
|
||||||
|
- file: "gfonts://Roboto"
|
||||||
|
id: font_status
|
||||||
|
size: 28
|
||||||
|
glyphs: &sk_glyphs "ÁÄČĎÉÍĹĽŇÓÔŔŠŤÚÝŽáäčďéíĺľňóôŕšťúýž°·"
|
||||||
|
glyphsets: [GF_Latin_Kernel]
|
||||||
|
- file: "gfonts://Roboto"
|
||||||
|
id: font_small
|
||||||
|
size: 24
|
||||||
|
glyphs: *sk_glyphs
|
||||||
|
glyphsets: [GF_Latin_Kernel]
|
||||||
|
- file: "gfonts://Roboto"
|
||||||
|
id: font_footer
|
||||||
|
size: 18
|
||||||
|
glyphs: "0123456789abcdef:. "
|
||||||
|
- file: "gfonts://Roboto@500"
|
||||||
|
id: font_date
|
||||||
|
size: 34
|
||||||
|
glyphs: *sk_glyphs
|
||||||
|
glyphsets: [GF_Latin_Kernel]
|
||||||
|
|
||||||
|
color:
|
||||||
|
- id: ink
|
||||||
|
red: 0%
|
||||||
|
green: 0%
|
||||||
|
blue: 0%
|
||||||
|
- id: paper
|
||||||
|
red: 100%
|
||||||
|
green: 100%
|
||||||
|
blue: 100%
|
||||||
|
|
||||||
|
display:
|
||||||
|
- platform: it8951
|
||||||
|
id: m5paper_display
|
||||||
|
model: m5stack-m5paper
|
||||||
|
rotation: 270
|
||||||
|
update_interval: never
|
||||||
|
# 1bpp: updates use the fast DU waveform, full GC16 refresh every 30 updates
|
||||||
|
grayscale: false
|
||||||
|
dithering: false
|
||||||
|
lambda: |-
|
||||||
|
// 540x960 portrait
|
||||||
|
it.fill(id(paper));
|
||||||
|
const int w = it.get_width();
|
||||||
|
const int cx = w / 2;
|
||||||
|
const float batt = id(battery_level).state;
|
||||||
|
|
||||||
|
// status screen after power-on, until Home Assistant connects
|
||||||
|
if (id(show_status) && !id(calendar_page)) {
|
||||||
|
it.image(cx, 200, id(esphome_logo), ImageAlign::CENTER);
|
||||||
|
it.print(cx, 320, id(font_title), id(ink), TextAlign::CENTER, "${name}");
|
||||||
|
|
||||||
|
const bool wifi_ok = wifi::global_wifi_component->is_connected();
|
||||||
|
if (wifi_ok) {
|
||||||
|
it.printf(40, 440, id(font_status), id(ink), "WiFi: connected (%d dBm)", wifi::global_wifi_component->wifi_rssi());
|
||||||
|
// IPv6 first (skip link-local), then IPv4
|
||||||
|
int y = 484;
|
||||||
|
char buf[network::IP_ADDRESS_BUFFER_SIZE];
|
||||||
|
for (bool v6 : {true, false}) {
|
||||||
|
for (auto &ip : wifi::global_wifi_component->get_ip_addresses()) {
|
||||||
|
if (!ip.is_set() || ip.is_ip6() != v6 || y > 590)
|
||||||
|
continue;
|
||||||
|
ip.str_to(buf);
|
||||||
|
if (v6 && strncmp(buf, "fe80", 4) == 0)
|
||||||
|
continue;
|
||||||
|
it.print(60, y, id(font_small), id(ink), buf);
|
||||||
|
y += 34;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
it.print(40, 440, id(font_status), id(ink), "WiFi: connecting...");
|
||||||
|
}
|
||||||
|
const bool api_ok = api::global_api_server->is_connected_with_state_subscription();
|
||||||
|
it.print(40, 620, id(font_status), id(ink),
|
||||||
|
api_ok ? "Home Assistant: connected" : "Home Assistant: waiting...");
|
||||||
|
if (!std::isnan(batt))
|
||||||
|
it.printf(40, 670, id(font_status), id(ink), "Battery: %.0f%% (%.2f V)", batt, id(battery_voltage).state);
|
||||||
|
it.printf(40, 720, id(font_status), id(ink), "Calendar: %u events stored", m5paper_calendar::events().count);
|
||||||
|
it.printf(40, 900, id(font_small), id(ink), "ESPHome %s", ESPHOME_VERSION);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calendar screen
|
||||||
|
using namespace m5paper_calendar;
|
||||||
|
image::Image *icons[] = {id(icon_birthday), id(icon_waste)};
|
||||||
|
image::Image *marks[] = {id(mark_birthday), id(mark_waste)};
|
||||||
|
static const char *const DAYS[] = {"Pondelok", "Utorok", "Streda", "Štvrtok", "Piatok", "Sobota", "Nedeľa"};
|
||||||
|
static const char *const DAYS_SHORT[] = {"Po", "Ut", "St", "Št", "Pi", "So", "Ne"};
|
||||||
|
static const char *const MONTHS[] = {"Január", "Február", "Marec", "Apríl", "Máj", "Jún",
|
||||||
|
"Júl", "August", "September", "Október", "November", "December"};
|
||||||
|
|
||||||
|
const auto &ev = m5paper_calendar::events();
|
||||||
|
const auto now = id(rtc_time).now();
|
||||||
|
if (!now.is_valid()) {
|
||||||
|
it.print(cx, 400, id(font_status), id(ink), TextAlign::CENTER, "Čas nie je k dispozícii");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const int32_t today = local_day(now);
|
||||||
|
|
||||||
|
// header: date, battery
|
||||||
|
it.printf(24, 16, id(font_date), id(ink), "%s %d. %d.", DAYS[weekday(today)], now.day_of_month, now.month);
|
||||||
|
if (!std::isnan(batt))
|
||||||
|
it.printf(w - 24, 22, id(font_small), id(ink), TextAlign::TOP_RIGHT, "%.0f %%", batt);
|
||||||
|
|
||||||
|
// month grid; days with events get the icons of their kinds
|
||||||
|
const int32_t first = days_from_civil(now.year, now.month, 1);
|
||||||
|
const int32_t next = now.month == 12 ? days_from_civil(now.year + 1, 1, 1) : days_from_civil(now.year, now.month + 1, 1);
|
||||||
|
const int dim = next - first, lead = weekday(first);
|
||||||
|
uint8_t kinds[32] = {};
|
||||||
|
for (int i = 0; i < ev.count; i++) {
|
||||||
|
if (ev.items[i].day >= first && ev.items[i].day < next)
|
||||||
|
kinds[ev.items[i].day - first + 1] |= 1 << ev.items[i].kind;
|
||||||
|
}
|
||||||
|
const int col_w = 70, row_h = 54, gx = (w - 7 * col_w) / 2, gy = 150;
|
||||||
|
it.printf(cx, 70, id(font_status), id(ink), TextAlign::TOP_CENTER, "%s %d", MONTHS[now.month - 1], now.year);
|
||||||
|
for (int c = 0; c < 7; c++)
|
||||||
|
it.print(gx + c * col_w + col_w / 2, 116, id(font_small), id(ink), TextAlign::TOP_CENTER, DAYS_SHORT[c]);
|
||||||
|
for (int d = 1; d <= dim; d++) {
|
||||||
|
const int cell = lead + d - 1;
|
||||||
|
const int x = gx + (cell % 7) * col_w, y = gy + (cell / 7) * row_h;
|
||||||
|
const bool is_today = first + d - 1 == today;
|
||||||
|
if (is_today)
|
||||||
|
it.filled_rectangle(x + 3, y + 1, col_w - 6, row_h - 2, id(ink));
|
||||||
|
const Color fg = is_today ? id(paper) : id(ink);
|
||||||
|
it.printf(x + col_w / 2, y + 4, id(font_small), fg, TextAlign::TOP_CENTER, "%d", d);
|
||||||
|
const int n = __builtin_popcount(kinds[d]);
|
||||||
|
int mx = x + (col_w - n * 20) / 2;
|
||||||
|
for (int k = 0; k < 2; k++) {
|
||||||
|
if (kinds[d] & (1 << k)) {
|
||||||
|
it.image(mx, y + 32, marks[k], fg);
|
||||||
|
mx += 20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// next 14 days; waste collection today or tomorrow is inverted: put the bin out
|
||||||
|
const int top = 490, list_h = 50, max_rows = 8;
|
||||||
|
it.horizontal_line(24, top - 12, w - 48, id(ink));
|
||||||
|
int rows = 0, more = 0;
|
||||||
|
for (int i = 0; i < ev.count; i++) {
|
||||||
|
const Event &e = ev.items[i];
|
||||||
|
if (e.day < today || e.day >= today + 14)
|
||||||
|
continue;
|
||||||
|
if (rows == max_rows) {
|
||||||
|
more++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const int y = top + rows++ * list_h;
|
||||||
|
const bool urgent = e.kind == WASTE && e.day <= today + 1;
|
||||||
|
if (urgent)
|
||||||
|
it.filled_rectangle(16, y, w - 32, list_h - 4, id(ink));
|
||||||
|
const Color fg = urgent ? id(paper) : id(ink);
|
||||||
|
const int ty = y + (list_h - 4) / 2;
|
||||||
|
if (e.day == today)
|
||||||
|
it.print(28, ty, id(font_small), fg, TextAlign::CENTER_LEFT, "Dnes");
|
||||||
|
else if (e.day == today + 1)
|
||||||
|
it.print(28, ty, id(font_small), fg, TextAlign::CENTER_LEFT, "Zajtra");
|
||||||
|
else {
|
||||||
|
const auto t = ESPTime::from_epoch_local(uint32_t(e.day) * 86400 + 43200);
|
||||||
|
it.printf(28, ty, id(font_small), fg, TextAlign::CENTER_LEFT, "%s %d. %d.", DAYS_SHORT[weekday(e.day)],
|
||||||
|
t.day_of_month, t.month);
|
||||||
|
}
|
||||||
|
if (e.kind < 2)
|
||||||
|
it.image(150, ty - 16, icons[e.kind], fg);
|
||||||
|
it.start_clipping(194, y, w - 28, y + list_h);
|
||||||
|
if (e.minute == ALL_DAY)
|
||||||
|
it.print(194, ty, id(font_small), fg, TextAlign::CENTER_LEFT, e.summary);
|
||||||
|
else
|
||||||
|
it.printf(194, ty, id(font_small), fg, TextAlign::CENTER_LEFT, "%02d:%02d %s", e.minute / 60, e.minute % 60,
|
||||||
|
e.summary);
|
||||||
|
it.end_clipping();
|
||||||
|
}
|
||||||
|
if (!ev.valid())
|
||||||
|
it.print(cx, top + 20, id(font_small), id(ink), TextAlign::TOP_CENTER, "Kalendár nie je k dispozícii");
|
||||||
|
else if (rows == 0)
|
||||||
|
it.print(cx, top + 20, id(font_small), id(ink), TextAlign::TOP_CENTER, "Žiadne udalosti v najbližších 14 dňoch");
|
||||||
|
else if (more > 0)
|
||||||
|
it.printf(w - 28, top + rows * list_h, id(font_small), id(ink), TextAlign::TOP_RIGHT, "+%d ďalšie", more);
|
||||||
|
|
||||||
|
// footer: IPv6 address (only while WiFi is up), last download
|
||||||
|
it.horizontal_line(24, 910, w - 48, id(ink));
|
||||||
|
char buf[network::IP_ADDRESS_BUFFER_SIZE];
|
||||||
|
for (auto &ip : wifi::global_wifi_component->get_ip_addresses()) {
|
||||||
|
if (!ip.is_set() || !ip.is_ip6())
|
||||||
|
continue;
|
||||||
|
ip.str_to(buf);
|
||||||
|
if (strncmp(buf, "fe80", 4) != 0) {
|
||||||
|
it.print(24, 944, id(font_footer), id(ink), TextAlign::BOTTOM_LEFT, buf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ev.valid()) {
|
||||||
|
const auto f = ESPTime::from_epoch_local(ev.fetched_at);
|
||||||
|
it.printf(w - 24, 944, id(font_footer), id(ink), TextAlign::BOTTOM_RIGHT, "%d. %d. %02d:%02d", f.day_of_month,
|
||||||
|
f.month, f.hour, f.minute);
|
||||||
|
}
|
||||||
|
|
||||||
|
touchscreen:
|
||||||
|
- platform: gt911
|
||||||
|
id: gt911_touchscreen
|
||||||
|
display: m5paper_display
|
||||||
|
# GPIO36 is input-only; INT is only driven as output when reset_pin is set
|
||||||
|
interrupt_pin:
|
||||||
|
number: GPIO36
|
||||||
|
mode:
|
||||||
|
input: true
|
||||||
|
output: false
|
||||||
@@ -26,6 +26,7 @@ esp32:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
ipv6_only: !include common/ipv6_only.yaml
|
||||||
|
|
||||||
# PINOUTS: M5ATOM
|
# PINOUTS: M5ATOM
|
||||||
# IR G12
|
# IR G12
|
||||||
@@ -228,9 +229,7 @@ button:
|
|||||||
- pzemac.reset_energy: pzemac_l3
|
- pzemac.reset_energy: pzemac_l3
|
||||||
|
|
||||||
time:
|
time:
|
||||||
- platform: homeassistant
|
- id: !extend ha_time
|
||||||
id: ha_time
|
|
||||||
timezone: Europe/Bratislava
|
|
||||||
on_time:
|
on_time:
|
||||||
- cron: '0 0 0 * * *'
|
- cron: '0 0 0 * * *'
|
||||||
then:
|
then:
|
||||||
|
|||||||
+8
-168
@@ -11,103 +11,19 @@ esphome:
|
|||||||
friendly_name: ${name}
|
friendly_name: ${name}
|
||||||
area: ${area}
|
area: ${area}
|
||||||
comment: ${comment}
|
comment: ${comment}
|
||||||
on_boot:
|
|
||||||
then:
|
|
||||||
- switch.turn_off: screen_power
|
|
||||||
- delay: 1s
|
|
||||||
- switch.turn_on: screen_power
|
|
||||||
- delay: 2s
|
|
||||||
- lambda: 'id(nspanel_id).set_display_dim(5, 80);'
|
|
||||||
|
|
||||||
esp32:
|
|
||||||
variant: esp32
|
|
||||||
toolchain: esp-idf
|
|
||||||
framework:
|
|
||||||
type: esp-idf
|
|
||||||
advanced:
|
|
||||||
minimum_chip_revision: 3.0
|
|
||||||
sram1_as_iram: true
|
|
||||||
|
|
||||||
external_components:
|
|
||||||
- source:
|
|
||||||
type: git
|
|
||||||
url: https://github.com/randybb/esphome-nspanel-lovelace-native
|
|
||||||
ref: devices
|
|
||||||
refresh: 0s
|
|
||||||
components: [nspanel_lovelace]
|
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
nspanel: !include
|
||||||
script:
|
file: common/nspanel.yaml
|
||||||
- id: apply_sleep_dim
|
vars:
|
||||||
then:
|
temperature_entity: sensor.zigbee_bedroom_temperature
|
||||||
- lambda: |-
|
sleep_dim: !include
|
||||||
uint8_t dim;
|
file: common/nspanel_sleep_dim.yaml
|
||||||
if (id(bedroom_lights).state) dim = 5; // sleepOverride
|
vars:
|
||||||
else if (id(sun_id).is_above_horizon(-6)) dim = 5; // day
|
sleep_override_entity: light.bedroom_lights
|
||||||
else dim = 0; // night
|
|
||||||
id(nspanel_id).set_display_inactive_dim(dim);
|
|
||||||
|
|
||||||
api:
|
|
||||||
homeassistant_services: true
|
|
||||||
homeassistant_states: true
|
|
||||||
actions:
|
|
||||||
- action: play_rtttl
|
|
||||||
variables:
|
|
||||||
song: string
|
|
||||||
then:
|
|
||||||
- rtttl.play:
|
|
||||||
rtttl: !lambda 'return song;'
|
|
||||||
|
|
||||||
- action: notify
|
|
||||||
variables:
|
|
||||||
heading: string
|
|
||||||
message: string
|
|
||||||
song: string
|
|
||||||
then:
|
|
||||||
- lambda: 'id(nspanel_id).notify_on_screensaver(heading, message, 10000);'
|
|
||||||
- rtttl.play:
|
|
||||||
rtttl: !lambda 'return song;'
|
|
||||||
|
|
||||||
- action: notify_fullscreen
|
|
||||||
variables:
|
|
||||||
title: string
|
|
||||||
description: string
|
|
||||||
button1: string
|
|
||||||
button2: string
|
|
||||||
timeout: int
|
|
||||||
icon: string
|
|
||||||
then:
|
|
||||||
- lambda: 'id(nspanel_id).notify_on_screensaver(title, description, timeout);'
|
|
||||||
- rtttl.play: "scale_up:d=32,o=5,b=100:c,c#,d#,e,f#,g#,a#,b"
|
|
||||||
|
|
||||||
uart:
|
|
||||||
id: display_uart
|
|
||||||
tx_pin: 16
|
|
||||||
rx_pin: 17
|
|
||||||
baud_rate: 115200
|
|
||||||
|
|
||||||
nspanel_lovelace:
|
nspanel_lovelace:
|
||||||
id: nspanel_id
|
|
||||||
sleep_timeout: 10
|
|
||||||
experimental_vertical_ui: true
|
|
||||||
locale:
|
|
||||||
language: sk
|
|
||||||
temperature_unit: celcius
|
|
||||||
screensaver:
|
|
||||||
time_id: ha_time
|
|
||||||
time_format: '%H:%M'
|
|
||||||
date_format: '%A, %d. %b. %Y'
|
|
||||||
weather:
|
|
||||||
entity_id: sensor.weather_forecast_daily
|
|
||||||
icon_color: [255, 255, 255]
|
|
||||||
entities:
|
|
||||||
- entity_id: sensor.zigbee_bedroom_temperature
|
|
||||||
icon:
|
|
||||||
value: home-thermometer-outline
|
|
||||||
color: [255, 255, 255]
|
|
||||||
precision: 1
|
|
||||||
cards:
|
cards:
|
||||||
- type: cardGrid
|
- type: cardGrid
|
||||||
title: Spálňa
|
title: Spálňa
|
||||||
@@ -135,13 +51,6 @@ nspanel_lovelace:
|
|||||||
title: Kúrenie
|
title: Kúrenie
|
||||||
thermo_entity_id: climate.bt_trv_bedroom
|
thermo_entity_id: climate.bt_trv_bedroom
|
||||||
|
|
||||||
time:
|
|
||||||
- platform: homeassistant
|
|
||||||
id: ha_time
|
|
||||||
timezone: Europe/Bratislava
|
|
||||||
on_time_sync:
|
|
||||||
- script.execute: apply_sleep_dim
|
|
||||||
|
|
||||||
binary_sensor:
|
binary_sensor:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
name: Left Button
|
name: Left Button
|
||||||
@@ -166,12 +75,6 @@ binary_sensor:
|
|||||||
entity_id: light.bedroom_lamps
|
entity_id: light.bedroom_lamps
|
||||||
- lambda: 'id(nspanel_id).render_default_page();'
|
- lambda: 'id(nspanel_id).render_default_page();'
|
||||||
|
|
||||||
- platform: homeassistant # sleepOverride
|
|
||||||
id: bedroom_lights
|
|
||||||
entity_id: light.bedroom_lights
|
|
||||||
on_state:
|
|
||||||
- script.execute: apply_sleep_dim
|
|
||||||
|
|
||||||
light:
|
light:
|
||||||
- platform: binary
|
- platform: binary
|
||||||
name: Light
|
name: Light
|
||||||
@@ -179,10 +82,6 @@ light:
|
|||||||
output: relay_1
|
output: relay_1
|
||||||
|
|
||||||
output:
|
output:
|
||||||
- platform: ledc
|
|
||||||
id: buzzer_out
|
|
||||||
pin:
|
|
||||||
number: 21
|
|
||||||
- id: relay_1
|
- id: relay_1
|
||||||
platform: gpio
|
platform: gpio
|
||||||
pin:
|
pin:
|
||||||
@@ -191,62 +90,3 @@ output:
|
|||||||
# platform: gpio
|
# platform: gpio
|
||||||
# pin:
|
# pin:
|
||||||
# number: 19
|
# number: 19
|
||||||
|
|
||||||
sun:
|
|
||||||
id: sun_id
|
|
||||||
time_id: ha_time
|
|
||||||
latitude: 48.15°
|
|
||||||
longitude: 17.11°
|
|
||||||
on_sunrise:
|
|
||||||
- elevation: -6°
|
|
||||||
then:
|
|
||||||
- script.execute: apply_sleep_dim
|
|
||||||
on_sunset:
|
|
||||||
- elevation: -6°
|
|
||||||
then:
|
|
||||||
- script.execute: apply_sleep_dim
|
|
||||||
|
|
||||||
switch:
|
|
||||||
- platform: gpio
|
|
||||||
name: Screen Power
|
|
||||||
id: screen_power
|
|
||||||
entity_category: config
|
|
||||||
pin:
|
|
||||||
number: 4
|
|
||||||
inverted: true
|
|
||||||
restore_mode: ALWAYS_ON
|
|
||||||
|
|
||||||
rtttl:
|
|
||||||
id: buzzer
|
|
||||||
output: buzzer_out
|
|
||||||
|
|
||||||
sensor:
|
|
||||||
- platform: adc
|
|
||||||
id: ntc_source
|
|
||||||
pin: 38
|
|
||||||
update_interval: 5min
|
|
||||||
attenuation: 12db
|
|
||||||
|
|
||||||
- platform: resistance
|
|
||||||
id: resistance_sensor
|
|
||||||
sensor: ntc_source
|
|
||||||
configuration: DOWNSTREAM
|
|
||||||
resistor: 11.2kOhm
|
|
||||||
|
|
||||||
- platform: ntc
|
|
||||||
name: Temperature
|
|
||||||
id: temperature
|
|
||||||
sensor: resistance_sensor
|
|
||||||
calibration:
|
|
||||||
b_constant: 3950
|
|
||||||
reference_temperature: 25°C
|
|
||||||
reference_resistance: 10kOhm
|
|
||||||
|
|
||||||
text_sensor:
|
|
||||||
- platform: homeassistant
|
|
||||||
id: name_day
|
|
||||||
entity_id: sensor.nameday_sk
|
|
||||||
on_value:
|
|
||||||
- lambda: |-
|
|
||||||
id(nspanel_id).set_time_format("%H:%M~" + x);
|
|
||||||
id(nspanel_id).update_time();
|
|
||||||
@@ -59,6 +59,7 @@ external_components:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
ipv6_only: !include common/ipv6_only.yaml
|
||||||
# common: !include common/common_dev.yaml
|
# common: !include common/common_dev.yaml
|
||||||
|
|
||||||
# web_server:
|
# web_server:
|
||||||
@@ -540,11 +541,6 @@ sensor:
|
|||||||
- platform: template
|
- platform: template
|
||||||
id: room_temperature_id
|
id: room_temperature_id
|
||||||
|
|
||||||
time:
|
|
||||||
platform: homeassistant
|
|
||||||
id: ha_time
|
|
||||||
timezone: Europe/Bratislava
|
|
||||||
|
|
||||||
text_sensor:
|
text_sensor:
|
||||||
- platform: homeassistant
|
- platform: homeassistant
|
||||||
id: name_day
|
id: name_day
|
||||||
|
|||||||
+5
-136
@@ -10,93 +10,16 @@ esphome:
|
|||||||
friendly_name: ${name}
|
friendly_name: ${name}
|
||||||
area: ${area}
|
area: ${area}
|
||||||
comment: ${comment}
|
comment: ${comment}
|
||||||
on_boot:
|
|
||||||
then:
|
|
||||||
- switch.turn_off: screen_power
|
|
||||||
- delay: 1s
|
|
||||||
- switch.turn_on: screen_power
|
|
||||||
- delay: 2s
|
|
||||||
- lambda: 'id(nspanel_id).set_display_dim(5, 80);'
|
|
||||||
|
|
||||||
esp32:
|
|
||||||
variant: esp32
|
|
||||||
toolchain: esp-idf
|
|
||||||
framework:
|
|
||||||
type: esp-idf
|
|
||||||
advanced:
|
|
||||||
minimum_chip_revision: 3.0
|
|
||||||
sram1_as_iram: true
|
|
||||||
|
|
||||||
external_components:
|
|
||||||
- source:
|
|
||||||
type: git
|
|
||||||
url: https://github.com/randybb/esphome-nspanel-lovelace-native
|
|
||||||
ref: devices
|
|
||||||
refresh: 0s
|
|
||||||
components: [nspanel_lovelace]
|
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
nspanel: !include
|
||||||
api:
|
file: common/nspanel.yaml
|
||||||
homeassistant_services: true
|
vars:
|
||||||
homeassistant_states: true
|
temperature_entity: sensor.zigbee_living_room_kitchen_temperature
|
||||||
actions:
|
temperature_name: Living Room Temperature
|
||||||
- action: play_rtttl
|
|
||||||
variables:
|
|
||||||
song: string
|
|
||||||
then:
|
|
||||||
- rtttl.play:
|
|
||||||
rtttl: !lambda 'return song;'
|
|
||||||
|
|
||||||
- action: notify
|
|
||||||
variables:
|
|
||||||
heading: string
|
|
||||||
message: string
|
|
||||||
song: string
|
|
||||||
then:
|
|
||||||
- lambda: 'id(nspanel_id).notify_on_screensaver(heading, message, 10000);'
|
|
||||||
- rtttl.play:
|
|
||||||
rtttl: !lambda 'return song;'
|
|
||||||
|
|
||||||
- action: notify_fullscreen
|
|
||||||
variables:
|
|
||||||
title: string
|
|
||||||
description: string
|
|
||||||
button1: string
|
|
||||||
button2: string
|
|
||||||
timeout: int
|
|
||||||
icon: string
|
|
||||||
then:
|
|
||||||
- lambda: 'id(nspanel_id).notify_on_screensaver(title, description, timeout);'
|
|
||||||
- rtttl.play: "scale_up:d=32,o=5,b=100:c,c#,d#,e,f#,g#,a#,b"
|
|
||||||
|
|
||||||
uart:
|
|
||||||
id: display_uart
|
|
||||||
tx_pin: 16
|
|
||||||
rx_pin: 17
|
|
||||||
baud_rate: 115200
|
|
||||||
|
|
||||||
nspanel_lovelace:
|
nspanel_lovelace:
|
||||||
id: nspanel_id
|
|
||||||
sleep_timeout: 10
|
|
||||||
experimental_vertical_ui: true
|
|
||||||
locale:
|
|
||||||
language: sk
|
|
||||||
temperature_unit: celcius
|
|
||||||
screensaver:
|
|
||||||
time_id: ha_time
|
|
||||||
time_format: '%H:%M'
|
|
||||||
date_format: '%A, %d. %b. %Y'
|
|
||||||
weather:
|
|
||||||
entity_id: sensor.weather_forecast_daily
|
|
||||||
icon_color: [255, 255, 255]
|
|
||||||
entities:
|
|
||||||
- entity_id: sensor.zigbee_living_room_kitchen_temperature
|
|
||||||
icon:
|
|
||||||
value: home-thermometer-outline
|
|
||||||
color: [255, 255, 255]
|
|
||||||
precision: 1
|
|
||||||
cards:
|
cards:
|
||||||
- type: cardGrid
|
- type: cardGrid
|
||||||
title: Kuchyňa / Obývačka
|
title: Kuchyňa / Obývačka
|
||||||
@@ -185,11 +108,6 @@ nspanel_lovelace:
|
|||||||
- name: Auto-zatváranie
|
- name: Auto-zatváranie
|
||||||
entity_id: switch.gate_auto_close
|
entity_id: switch.gate_auto_close
|
||||||
|
|
||||||
time:
|
|
||||||
- platform: homeassistant
|
|
||||||
id: ha_time
|
|
||||||
timezone: Europe/Bratislava
|
|
||||||
|
|
||||||
binary_sensor:
|
binary_sensor:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
name: Left Button
|
name: Left Button
|
||||||
@@ -240,11 +158,6 @@ binary_sensor:
|
|||||||
entity_id: light.mcu_kitchen_stove
|
entity_id: light.mcu_kitchen_stove
|
||||||
- lambda: 'id(nspanel_id).render_default_page();'
|
- lambda: 'id(nspanel_id).render_default_page();'
|
||||||
- lambda: 'id(nspanel_id).render_next_page();'
|
- lambda: 'id(nspanel_id).render_next_page();'
|
||||||
output:
|
|
||||||
- platform: ledc
|
|
||||||
id: buzzer_out
|
|
||||||
pin:
|
|
||||||
number: 21
|
|
||||||
|
|
||||||
switch:
|
switch:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
@@ -259,47 +172,3 @@ switch:
|
|||||||
entity_category: config
|
entity_category: config
|
||||||
pin:
|
pin:
|
||||||
number: 19
|
number: 19
|
||||||
|
|
||||||
- platform: gpio
|
|
||||||
name: Screen Power
|
|
||||||
id: screen_power
|
|
||||||
entity_category: config
|
|
||||||
pin:
|
|
||||||
number: 4
|
|
||||||
inverted: true
|
|
||||||
restore_mode: ALWAYS_ON
|
|
||||||
|
|
||||||
rtttl:
|
|
||||||
id: buzzer
|
|
||||||
output: buzzer_out
|
|
||||||
|
|
||||||
sensor:
|
|
||||||
- platform: adc
|
|
||||||
id: ntc_source
|
|
||||||
pin: 38
|
|
||||||
update_interval: 5min
|
|
||||||
attenuation: 12db
|
|
||||||
|
|
||||||
- platform: resistance
|
|
||||||
id: resistance_sensor
|
|
||||||
sensor: ntc_source
|
|
||||||
configuration: DOWNSTREAM
|
|
||||||
resistor: 11.2kOhm
|
|
||||||
|
|
||||||
- platform: ntc
|
|
||||||
name: "Living Room Temperature"
|
|
||||||
id: temperature
|
|
||||||
sensor: resistance_sensor
|
|
||||||
calibration:
|
|
||||||
b_constant: 3950
|
|
||||||
reference_temperature: 25°C
|
|
||||||
reference_resistance: 10kOhm
|
|
||||||
|
|
||||||
text_sensor:
|
|
||||||
- platform: homeassistant
|
|
||||||
id: name_day
|
|
||||||
entity_id: sensor.nameday_sk
|
|
||||||
on_value:
|
|
||||||
- lambda: |-
|
|
||||||
id(nspanel_id).set_time_format("%H:%M~" + x);
|
|
||||||
id(nspanel_id).update_time();
|
|
||||||
+9
-137
@@ -10,93 +10,20 @@ esphome:
|
|||||||
friendly_name: ${name}
|
friendly_name: ${name}
|
||||||
area: ${area}
|
area: ${area}
|
||||||
comment: ${comment}
|
comment: ${comment}
|
||||||
on_boot:
|
|
||||||
then:
|
|
||||||
- switch.turn_off: screen_power
|
|
||||||
- delay: 1s
|
|
||||||
- switch.turn_on: screen_power
|
|
||||||
- delay: 2s
|
|
||||||
- lambda: 'id(nspanel_id).set_display_dim(5, 80);'
|
|
||||||
|
|
||||||
esp32:
|
|
||||||
variant: esp32
|
|
||||||
toolchain: esp-idf
|
|
||||||
framework:
|
|
||||||
type: esp-idf
|
|
||||||
advanced:
|
|
||||||
minimum_chip_revision: 3.0
|
|
||||||
sram1_as_iram: true
|
|
||||||
|
|
||||||
external_components:
|
|
||||||
- source:
|
|
||||||
type: git
|
|
||||||
url: https://github.com/randybb/esphome-nspanel-lovelace-native
|
|
||||||
ref: devices
|
|
||||||
refresh: 0s
|
|
||||||
components: [nspanel_lovelace]
|
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
nspanel: !include
|
||||||
api:
|
file: common/nspanel.yaml
|
||||||
homeassistant_services: true
|
vars:
|
||||||
homeassistant_states: true
|
temperature_entity: sensor.zigbee_office_j_temperature
|
||||||
actions:
|
ntc_reference_temperature: 22°C
|
||||||
- action: play_rtttl
|
sleep_dim: !include
|
||||||
variables:
|
file: common/nspanel_sleep_dim.yaml
|
||||||
song: string
|
vars:
|
||||||
then:
|
sleep_override_entity: light.office_j_lights
|
||||||
- rtttl.play:
|
|
||||||
rtttl: !lambda 'return song;'
|
|
||||||
|
|
||||||
- action: notify
|
|
||||||
variables:
|
|
||||||
heading: string
|
|
||||||
message: string
|
|
||||||
song: string
|
|
||||||
then:
|
|
||||||
- lambda: 'id(nspanel_id).notify_on_screensaver(heading, message, 10000);'
|
|
||||||
- rtttl.play:
|
|
||||||
rtttl: !lambda 'return song;'
|
|
||||||
|
|
||||||
- action: notify_fullscreen
|
|
||||||
variables:
|
|
||||||
title: string
|
|
||||||
description: string
|
|
||||||
button1: string
|
|
||||||
button2: string
|
|
||||||
timeout: int
|
|
||||||
icon: string
|
|
||||||
then:
|
|
||||||
- lambda: 'id(nspanel_id).notify_on_screensaver(title, description, timeout);'
|
|
||||||
- rtttl.play: "scale_up:d=32,o=5,b=100:c,c#,d#,e,f#,g#,a#,b"
|
|
||||||
|
|
||||||
uart:
|
|
||||||
id: display_uart
|
|
||||||
tx_pin: 16
|
|
||||||
rx_pin: 17
|
|
||||||
baud_rate: 115200
|
|
||||||
|
|
||||||
nspanel_lovelace:
|
nspanel_lovelace:
|
||||||
id: nspanel_id
|
|
||||||
sleep_timeout: 10
|
|
||||||
experimental_vertical_ui: true
|
|
||||||
locale:
|
|
||||||
language: sk
|
|
||||||
temperature_unit: celcius
|
|
||||||
screensaver:
|
|
||||||
time_id: ha_time
|
|
||||||
time_format: '%H:%M'
|
|
||||||
date_format: '%A, %d. %b. %Y'
|
|
||||||
weather:
|
|
||||||
entity_id: sensor.weather_forecast_daily
|
|
||||||
icon_color: [255, 255, 255]
|
|
||||||
entities:
|
|
||||||
- entity_id: sensor.zigbee_office_j_temperature
|
|
||||||
icon:
|
|
||||||
value: home-thermometer-outline
|
|
||||||
color: [255, 255, 255]
|
|
||||||
precision: 1
|
|
||||||
cards:
|
cards:
|
||||||
- type: cardGrid
|
- type: cardGrid
|
||||||
title: Janko
|
title: Janko
|
||||||
@@ -134,11 +61,6 @@ nspanel_lovelace:
|
|||||||
- name: Auto-zatváranie
|
- name: Auto-zatváranie
|
||||||
entity_id: switch.gate_auto_close
|
entity_id: switch.gate_auto_close
|
||||||
|
|
||||||
time:
|
|
||||||
- platform: homeassistant
|
|
||||||
id: ha_time
|
|
||||||
timezone: Europe/Bratislava
|
|
||||||
|
|
||||||
binary_sensor:
|
binary_sensor:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
name: Left Button
|
name: Left Button
|
||||||
@@ -162,11 +84,6 @@ binary_sensor:
|
|||||||
- lambda: 'id(nspanel_id).render_default_page();'
|
- lambda: 'id(nspanel_id).render_default_page();'
|
||||||
|
|
||||||
output:
|
output:
|
||||||
- platform: ledc
|
|
||||||
id: buzzer_out
|
|
||||||
pin:
|
|
||||||
number: 21
|
|
||||||
|
|
||||||
- id: relay_1
|
- id: relay_1
|
||||||
platform: gpio
|
platform: gpio
|
||||||
pin:
|
pin:
|
||||||
@@ -185,48 +102,3 @@ light:
|
|||||||
# # - platform: binary
|
# # - platform: binary
|
||||||
# # name: Relay 2
|
# # name: Relay 2
|
||||||
# # output: relay_2
|
# # output: relay_2
|
||||||
|
|
||||||
switch:
|
|
||||||
- platform: gpio
|
|
||||||
name: Screen Power
|
|
||||||
id: screen_power
|
|
||||||
entity_category: config
|
|
||||||
pin:
|
|
||||||
number: 4
|
|
||||||
inverted: true
|
|
||||||
restore_mode: ALWAYS_ON
|
|
||||||
|
|
||||||
rtttl:
|
|
||||||
id: buzzer
|
|
||||||
output: buzzer_out
|
|
||||||
|
|
||||||
sensor:
|
|
||||||
- platform: adc
|
|
||||||
id: ntc_source
|
|
||||||
pin: 38
|
|
||||||
update_interval: 5min
|
|
||||||
attenuation: 12db
|
|
||||||
|
|
||||||
- platform: resistance
|
|
||||||
id: resistance_sensor
|
|
||||||
sensor: ntc_source
|
|
||||||
configuration: DOWNSTREAM
|
|
||||||
resistor: 11.2kOhm
|
|
||||||
|
|
||||||
- platform: ntc
|
|
||||||
name: Temperature
|
|
||||||
id: temperature
|
|
||||||
sensor: resistance_sensor
|
|
||||||
calibration:
|
|
||||||
b_constant: 3950
|
|
||||||
reference_temperature: 22°C
|
|
||||||
reference_resistance: 10kOhm
|
|
||||||
|
|
||||||
text_sensor:
|
|
||||||
- platform: homeassistant
|
|
||||||
id: name_day
|
|
||||||
entity_id: sensor.nameday_sk
|
|
||||||
on_value:
|
|
||||||
- lambda: |-
|
|
||||||
id(nspanel_id).set_time_format("%H:%M~" + x);
|
|
||||||
id(nspanel_id).update_time();
|
|
||||||
+6
-134
@@ -10,33 +10,15 @@ esphome:
|
|||||||
friendly_name: ${name}
|
friendly_name: ${name}
|
||||||
area: ${area}
|
area: ${area}
|
||||||
comment: ${comment}
|
comment: ${comment}
|
||||||
on_boot:
|
|
||||||
then:
|
|
||||||
- switch.turn_off: screen_power
|
|
||||||
- delay: 1s
|
|
||||||
- switch.turn_on: screen_power
|
|
||||||
- delay: 2s
|
|
||||||
- lambda: 'id(nspanel_id).set_display_dim(5, 80);'
|
|
||||||
|
|
||||||
esp32:
|
|
||||||
variant: esp32
|
|
||||||
toolchain: esp-idf
|
|
||||||
framework:
|
|
||||||
type: esp-idf
|
|
||||||
advanced:
|
|
||||||
minimum_chip_revision: 3.0
|
|
||||||
sram1_as_iram: true
|
|
||||||
|
|
||||||
external_components:
|
|
||||||
- source:
|
|
||||||
type: git
|
|
||||||
url: https://github.com/randybb/esphome-nspanel-lovelace-native
|
|
||||||
ref: devices
|
|
||||||
refresh: 0s
|
|
||||||
components: [nspanel_lovelace]
|
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
ipv6_only: !include common/ipv6_only.yaml
|
||||||
|
nspanel: !include
|
||||||
|
file: common/nspanel.yaml
|
||||||
|
vars:
|
||||||
|
temperature_entity: sensor.zigbee_office_r_temperature
|
||||||
|
ntc_reference_temperature: 22°C
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- id: geiger_click
|
- id: geiger_click
|
||||||
@@ -55,65 +37,7 @@ script:
|
|||||||
return esphome::random_uint32() % 450 + 50;
|
return esphome::random_uint32() % 450 + 50;
|
||||||
- script.execute: geiger_click
|
- script.execute: geiger_click
|
||||||
|
|
||||||
api:
|
|
||||||
homeassistant_services: true
|
|
||||||
homeassistant_states: true
|
|
||||||
actions:
|
|
||||||
- action: play_rtttl
|
|
||||||
variables:
|
|
||||||
song: string
|
|
||||||
then:
|
|
||||||
- rtttl.play:
|
|
||||||
rtttl: !lambda 'return song;'
|
|
||||||
|
|
||||||
- action: notify
|
|
||||||
variables:
|
|
||||||
heading: string
|
|
||||||
message: string
|
|
||||||
song: string
|
|
||||||
then:
|
|
||||||
- lambda: 'id(nspanel_id).notify_on_screensaver(heading, message, 10000);'
|
|
||||||
- rtttl.play:
|
|
||||||
rtttl: !lambda 'return song;'
|
|
||||||
|
|
||||||
- action: notify_fullscreen
|
|
||||||
variables:
|
|
||||||
title: string
|
|
||||||
description: string
|
|
||||||
button1: string
|
|
||||||
button2: string
|
|
||||||
timeout: int
|
|
||||||
icon: string
|
|
||||||
then:
|
|
||||||
- lambda: 'id(nspanel_id).notify_on_screensaver(title, description, timeout);'
|
|
||||||
- rtttl.play: "scale_up:d=32,o=5,b=100:c,c#,d#,e,f#,g#,a#,b"
|
|
||||||
|
|
||||||
uart:
|
|
||||||
id: display_uart
|
|
||||||
tx_pin: 16
|
|
||||||
rx_pin: 17
|
|
||||||
baud_rate: 115200
|
|
||||||
|
|
||||||
nspanel_lovelace:
|
nspanel_lovelace:
|
||||||
id: nspanel_id
|
|
||||||
sleep_timeout: 10
|
|
||||||
experimental_vertical_ui: true
|
|
||||||
locale:
|
|
||||||
language: sk
|
|
||||||
temperature_unit: celcius
|
|
||||||
screensaver:
|
|
||||||
time_id: ha_time
|
|
||||||
time_format: '%H:%M'
|
|
||||||
date_format: '%A, %d. %b. %Y'
|
|
||||||
weather:
|
|
||||||
entity_id: sensor.weather_forecast_daily
|
|
||||||
icon_color: [255, 255, 255]
|
|
||||||
entities:
|
|
||||||
- entity_id: sensor.zigbee_office_r_temperature
|
|
||||||
icon:
|
|
||||||
value: home-thermometer-outline
|
|
||||||
color: [255, 255, 255]
|
|
||||||
precision: 1
|
|
||||||
cards:
|
cards:
|
||||||
- type: cardGrid
|
- type: cardGrid
|
||||||
title: Romanko
|
title: Romanko
|
||||||
@@ -158,11 +82,6 @@ nspanel_lovelace:
|
|||||||
- name: Auto-zatváranie
|
- name: Auto-zatváranie
|
||||||
entity_id: switch.gate_auto_close
|
entity_id: switch.gate_auto_close
|
||||||
|
|
||||||
time:
|
|
||||||
- platform: homeassistant
|
|
||||||
id: ha_time
|
|
||||||
timezone: Europe/Bratislava
|
|
||||||
|
|
||||||
binary_sensor:
|
binary_sensor:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
name: Left Button
|
name: Left Button
|
||||||
@@ -186,10 +105,6 @@ binary_sensor:
|
|||||||
- lambda: 'id(nspanel_id).render_default_page();'
|
- lambda: 'id(nspanel_id).render_default_page();'
|
||||||
|
|
||||||
output:
|
output:
|
||||||
- platform: ledc
|
|
||||||
id: buzzer_out
|
|
||||||
pin:
|
|
||||||
number: 21
|
|
||||||
- id: relay_1
|
- id: relay_1
|
||||||
platform: gpio
|
platform: gpio
|
||||||
pin:
|
pin:
|
||||||
@@ -229,14 +144,6 @@ switch:
|
|||||||
id: relay_2
|
id: relay_2
|
||||||
pin:
|
pin:
|
||||||
number: 19
|
number: 19
|
||||||
- platform: gpio
|
|
||||||
name: Screen Power
|
|
||||||
id: screen_power
|
|
||||||
entity_category: config
|
|
||||||
pin:
|
|
||||||
number: 4
|
|
||||||
inverted: true
|
|
||||||
restore_mode: ALWAYS_ON
|
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "Geiger Counter"
|
name: "Geiger Counter"
|
||||||
optimistic: True
|
optimistic: True
|
||||||
@@ -244,38 +151,3 @@ switch:
|
|||||||
- script.execute: geiger_click
|
- script.execute: geiger_click
|
||||||
turn_off_action:
|
turn_off_action:
|
||||||
- script.stop: geiger_click
|
- script.stop: geiger_click
|
||||||
|
|
||||||
rtttl:
|
|
||||||
id: buzzer
|
|
||||||
output: buzzer_out
|
|
||||||
|
|
||||||
sensor:
|
|
||||||
- platform: adc
|
|
||||||
id: ntc_source
|
|
||||||
pin: 38
|
|
||||||
update_interval: 5min
|
|
||||||
attenuation: 12db
|
|
||||||
|
|
||||||
- platform: resistance
|
|
||||||
id: resistance_sensor
|
|
||||||
sensor: ntc_source
|
|
||||||
configuration: DOWNSTREAM
|
|
||||||
resistor: 11.2kOhm
|
|
||||||
|
|
||||||
- platform: ntc
|
|
||||||
name: Temperature
|
|
||||||
id: temperature
|
|
||||||
sensor: resistance_sensor
|
|
||||||
calibration:
|
|
||||||
b_constant: 3950
|
|
||||||
reference_temperature: 22°C
|
|
||||||
reference_resistance: 10kOhm
|
|
||||||
|
|
||||||
text_sensor:
|
|
||||||
- platform: homeassistant
|
|
||||||
id: name_day
|
|
||||||
entity_id: sensor.nameday_sk
|
|
||||||
on_value:
|
|
||||||
- lambda: |-
|
|
||||||
id(nspanel_id).set_time_format("%H:%M~" + x);
|
|
||||||
id(nspanel_id).update_time();
|
|
||||||
+12
-137
@@ -10,93 +10,16 @@ esphome:
|
|||||||
friendly_name: ${name}
|
friendly_name: ${name}
|
||||||
area: ${area}
|
area: ${area}
|
||||||
comment: ${comment}
|
comment: ${comment}
|
||||||
on_boot:
|
|
||||||
then:
|
|
||||||
- switch.turn_off: screen_power
|
|
||||||
- delay: 1s
|
|
||||||
- switch.turn_on: screen_power
|
|
||||||
- delay: 2s
|
|
||||||
- lambda: 'id(nspanel_id).set_display_dim(5, 80);'
|
|
||||||
|
|
||||||
esp32:
|
|
||||||
variant: esp32
|
|
||||||
toolchain: esp-idf
|
|
||||||
framework:
|
|
||||||
type: esp-idf
|
|
||||||
advanced:
|
|
||||||
minimum_chip_revision: 3.0
|
|
||||||
sram1_as_iram: true
|
|
||||||
|
|
||||||
external_components:
|
|
||||||
- source:
|
|
||||||
type: git
|
|
||||||
url: https://github.com/randybb/esphome-nspanel-lovelace-native
|
|
||||||
ref: devices
|
|
||||||
refresh: 0s
|
|
||||||
components: [nspanel_lovelace]
|
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
nspanel: !include
|
||||||
api:
|
file: common/nspanel.yaml
|
||||||
homeassistant_services: true
|
vars:
|
||||||
homeassistant_states: true
|
temperature_entity: sensor.zigbee_living_room_kitchen_temperature
|
||||||
actions:
|
temperature_name: Living Room Temperature
|
||||||
- action: play_rtttl
|
|
||||||
variables:
|
|
||||||
song: string
|
|
||||||
then:
|
|
||||||
- rtttl.play:
|
|
||||||
rtttl: !lambda 'return song;'
|
|
||||||
|
|
||||||
- action: notify
|
|
||||||
variables:
|
|
||||||
heading: string
|
|
||||||
message: string
|
|
||||||
song: string
|
|
||||||
then:
|
|
||||||
- lambda: 'id(nspanel_id).notify_on_screensaver(heading, message, 10000);'
|
|
||||||
- rtttl.play:
|
|
||||||
rtttl: !lambda 'return song;'
|
|
||||||
|
|
||||||
- action: notify_fullscreen
|
|
||||||
variables:
|
|
||||||
title: string
|
|
||||||
description: string
|
|
||||||
button1: string
|
|
||||||
button2: string
|
|
||||||
timeout: int
|
|
||||||
icon: string
|
|
||||||
then:
|
|
||||||
- lambda: 'id(nspanel_id).notify_on_screensaver(title, description, timeout);'
|
|
||||||
- rtttl.play: "scale_up:d=32,o=5,b=100:c,c#,d#,e,f#,g#,a#,b"
|
|
||||||
|
|
||||||
uart:
|
|
||||||
id: display_uart
|
|
||||||
tx_pin: 16
|
|
||||||
rx_pin: 17
|
|
||||||
baud_rate: 115200
|
|
||||||
|
|
||||||
nspanel_lovelace:
|
nspanel_lovelace:
|
||||||
id: nspanel_id
|
|
||||||
sleep_timeout: 10
|
|
||||||
experimental_vertical_ui: true
|
|
||||||
locale:
|
|
||||||
language: sk
|
|
||||||
temperature_unit: celcius
|
|
||||||
screensaver:
|
|
||||||
time_id: ha_time
|
|
||||||
time_format: '%H:%M'
|
|
||||||
date_format: '%A, %d. %b. %Y'
|
|
||||||
weather:
|
|
||||||
entity_id: sensor.weather_forecast_daily
|
|
||||||
icon_color: [255, 255, 255]
|
|
||||||
entities:
|
|
||||||
- entity_id: sensor.zigbee_living_room_kitchen_temperature
|
|
||||||
icon:
|
|
||||||
value: home-thermometer-outline
|
|
||||||
color: [255, 255, 255]
|
|
||||||
precision: 1
|
|
||||||
cards:
|
cards:
|
||||||
- type: cardGrid
|
- type: cardGrid
|
||||||
title: Kuchyňa / Obývačka
|
title: Kuchyňa / Obývačka
|
||||||
@@ -214,11 +137,6 @@ nspanel_lovelace:
|
|||||||
- name: Rast trávy
|
- name: Rast trávy
|
||||||
entity_id: automation.irrigation_grow_grass
|
entity_id: automation.irrigation_grow_grass
|
||||||
|
|
||||||
time:
|
|
||||||
- platform: homeassistant
|
|
||||||
id: ha_time
|
|
||||||
timezone: Europe/Bratislava
|
|
||||||
|
|
||||||
binary_sensor:
|
binary_sensor:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
name: Left Button
|
name: Left Button
|
||||||
@@ -238,8 +156,11 @@ binary_sensor:
|
|||||||
inverted: true
|
inverted: true
|
||||||
on_click:
|
on_click:
|
||||||
if:
|
if:
|
||||||
|
# mcu-patio online -> toggle its lights; otherwise (no API or relay_2 cut its power) the relay
|
||||||
condition:
|
condition:
|
||||||
api.connected:
|
and:
|
||||||
|
- api.connected:
|
||||||
|
- binary_sensor.is_on: patio_status
|
||||||
then:
|
then:
|
||||||
- homeassistant.action:
|
- homeassistant.action:
|
||||||
action: light.toggle
|
action: light.toggle
|
||||||
@@ -251,11 +172,9 @@ binary_sensor:
|
|||||||
else:
|
else:
|
||||||
- switch.toggle: relay_2
|
- switch.toggle: relay_2
|
||||||
|
|
||||||
output:
|
- platform: homeassistant
|
||||||
- platform: ledc
|
id: patio_status
|
||||||
id: buzzer_out
|
entity_id: binary_sensor.mcu_patio_status
|
||||||
pin:
|
|
||||||
number: 21
|
|
||||||
|
|
||||||
switch:
|
switch:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
@@ -271,47 +190,3 @@ switch:
|
|||||||
entity_category: config
|
entity_category: config
|
||||||
pin:
|
pin:
|
||||||
number: 19
|
number: 19
|
||||||
|
|
||||||
- platform: gpio
|
|
||||||
name: Screen Power
|
|
||||||
id: screen_power
|
|
||||||
entity_category: config
|
|
||||||
pin:
|
|
||||||
number: 4
|
|
||||||
inverted: true
|
|
||||||
restore_mode: ALWAYS_ON
|
|
||||||
|
|
||||||
rtttl:
|
|
||||||
id: buzzer
|
|
||||||
output: buzzer_out
|
|
||||||
|
|
||||||
sensor:
|
|
||||||
- platform: adc
|
|
||||||
id: ntc_source
|
|
||||||
pin: 38
|
|
||||||
update_interval: 5min
|
|
||||||
attenuation: 12db
|
|
||||||
|
|
||||||
- platform: resistance
|
|
||||||
id: resistance_sensor
|
|
||||||
sensor: ntc_source
|
|
||||||
configuration: DOWNSTREAM
|
|
||||||
resistor: 11.2kOhm
|
|
||||||
|
|
||||||
- platform: ntc
|
|
||||||
name: Living Room Temperature
|
|
||||||
id: temperature
|
|
||||||
sensor: resistance_sensor
|
|
||||||
calibration:
|
|
||||||
b_constant: 3950
|
|
||||||
reference_temperature: 25°C
|
|
||||||
reference_resistance: 10kOhm
|
|
||||||
|
|
||||||
text_sensor:
|
|
||||||
- platform: homeassistant
|
|
||||||
id: name_day
|
|
||||||
entity_id: sensor.nameday_sk
|
|
||||||
on_value:
|
|
||||||
- lambda: |-
|
|
||||||
id(nspanel_id).set_time_format("%H:%M~" + x);
|
|
||||||
id(nspanel_id).update_time();
|
|
||||||
@@ -22,6 +22,7 @@ esp32:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
ipv6_only: !include common/ipv6_only.yaml
|
||||||
|
|
||||||
# GPIO21 UART TX
|
# GPIO21 UART TX
|
||||||
# GPIO20 UART RX
|
# GPIO20 UART RX
|
||||||
|
|||||||
+1
-5
@@ -37,6 +37,7 @@ esp32:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
ipv6_only: !include common/ipv6_only.yaml
|
||||||
|
|
||||||
|
|
||||||
# PINOUTS: M5ATOM+Atom Hub Switch
|
# PINOUTS: M5ATOM+Atom Hub Switch
|
||||||
@@ -54,11 +55,6 @@ packages:
|
|||||||
# Relay1 G22
|
# Relay1 G22
|
||||||
# Relay2 G19
|
# Relay2 G19
|
||||||
|
|
||||||
time:
|
|
||||||
- platform: homeassistant
|
|
||||||
id: ha_time
|
|
||||||
timezone: Europe/Bratislava
|
|
||||||
|
|
||||||
i2c:
|
i2c:
|
||||||
# - id: i2c0
|
# - id: i2c0
|
||||||
# sda: 26
|
# sda: 26
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ esp32:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
ipv6_only: !include common/ipv6_only.yaml
|
||||||
|
|
||||||
uart:
|
uart:
|
||||||
- id: cse7766_uart
|
- id: cse7766_uart
|
||||||
@@ -187,5 +188,3 @@ switch:
|
|||||||
- switch.turn_off: relay_off # bi-stable relay so no need to keep on
|
- switch.turn_off: relay_off # bi-stable relay so no need to keep on
|
||||||
interlock: [relay_on]
|
interlock: [relay_on]
|
||||||
|
|
||||||
time:
|
|
||||||
- !include common/time/homeassistant.yaml
|
|
||||||
+1
-4
@@ -2,7 +2,7 @@
|
|||||||
substitutions:
|
substitutions:
|
||||||
device: pow-washer
|
device: pow-washer
|
||||||
name: Washer POW
|
name: Washer POW
|
||||||
comment:
|
comment: "spare"
|
||||||
area: Washroom
|
area: Washroom
|
||||||
update_interval: 10s
|
update_interval: 10s
|
||||||
|
|
||||||
@@ -186,6 +186,3 @@ switch:
|
|||||||
- delay: 500ms
|
- delay: 500ms
|
||||||
- switch.turn_off: relay_off # bi-stable relay so no need to keep on
|
- switch.turn_off: relay_off # bi-stable relay so no need to keep on
|
||||||
interlock: [relay_on]
|
interlock: [relay_on]
|
||||||
|
|
||||||
time:
|
|
||||||
- !include common/time/homeassistant.yaml
|
|
||||||
@@ -19,6 +19,7 @@ esp32:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
ipv6_only: !include common/ipv6_only.yaml
|
||||||
|
|
||||||
spi:
|
spi:
|
||||||
clk_pin: 23
|
clk_pin: 23
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ esp32:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
ipv6_only: !include common/ipv6_only.yaml
|
||||||
|
|
||||||
# logger:
|
# logger:
|
||||||
# hardware_uart: UART0
|
# hardware_uart: UART0
|
||||||
|
|||||||
+1
-2
@@ -21,6 +21,7 @@ esp32:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
ipv6_only: !include common/ipv6_only.yaml
|
||||||
|
|
||||||
i2c:
|
i2c:
|
||||||
sda: 25
|
sda: 25
|
||||||
@@ -109,5 +110,3 @@ switch:
|
|||||||
number: 19
|
number: 19
|
||||||
mode: OUTPUT
|
mode: OUTPUT
|
||||||
|
|
||||||
time:
|
|
||||||
- !include common/time/homeassistant.yaml
|
|
||||||
+1
-2
@@ -121,8 +121,7 @@ switch:
|
|||||||
# number: GPIO00
|
# number: GPIO00
|
||||||
# inverted: True
|
# inverted: True
|
||||||
|
|
||||||
time:
|
# time:
|
||||||
- !include common/time/homeassistant.yaml
|
|
||||||
# - platform: homeassistant
|
# - platform: homeassistant
|
||||||
# timezone: Europe/Bratislava
|
# timezone: Europe/Bratislava
|
||||||
# on_time:
|
# on_time:
|
||||||
|
|||||||
+1
-2
@@ -122,8 +122,7 @@ switch:
|
|||||||
on_turn_off:
|
on_turn_off:
|
||||||
- light.turn_off: light_red
|
- light.turn_off: light_red
|
||||||
|
|
||||||
time:
|
# time:
|
||||||
- !include common/time/homeassistant.yaml
|
|
||||||
# - platform: homeassistant
|
# - platform: homeassistant
|
||||||
# timezone: Europe/Bratislava
|
# timezone: Europe/Bratislava
|
||||||
# on_time:
|
# on_time:
|
||||||
|
|||||||
+1
-2
@@ -144,8 +144,7 @@ switch:
|
|||||||
# number: GPIO00
|
# number: GPIO00
|
||||||
# inverted: True
|
# inverted: True
|
||||||
|
|
||||||
time:
|
# time:
|
||||||
- !include common/time/homeassistant.yaml
|
|
||||||
# - platform: homeassistant
|
# - platform: homeassistant
|
||||||
# timezone: Europe/Bratislava
|
# timezone: Europe/Bratislava
|
||||||
# on_time:
|
# on_time:
|
||||||
|
|||||||
+25
-11
@@ -33,14 +33,15 @@ psram:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
ipv6_only: !include common/ipv6_only.yaml
|
||||||
|
|
||||||
external_components:
|
external_components:
|
||||||
- source: github://m5stack/esphome-yaml/components
|
- source: github://m5stack/esphome-yaml/components
|
||||||
components: [axp2101, aw88298, aw9523b, si12t, m5ioe1, ftservo, scs9009]
|
components: [axp2101, aw88298, aw9523b, si12t, m5ioe1, ftservo, scs9009]
|
||||||
refresh: 0s
|
refresh: 0s
|
||||||
- source: github://randybb/esphome-components
|
- source: github://randybb/esphome-components
|
||||||
components: [audio_spectrum]
|
components: [audio_spectrum, speaker] # speaker: esphome#19862, drop once merged
|
||||||
refresh: 1d
|
refresh: 1h
|
||||||
|
|
||||||
globals:
|
globals:
|
||||||
- id: in_love
|
- id: in_love
|
||||||
@@ -464,9 +465,9 @@ spi:
|
|||||||
|
|
||||||
image:
|
image:
|
||||||
- platform: file
|
- platform: file
|
||||||
file: assets/images/esphome-logo-text-on-dark.svg
|
file: assets/images/made-for-esphome-white-on-black.svg
|
||||||
id: esphome_logo
|
id: esphome_logo
|
||||||
resize: 280x56
|
resize: 230x68
|
||||||
type: RGB565
|
type: RGB565
|
||||||
transparency: alpha_channel
|
transparency: alpha_channel
|
||||||
- platform: file
|
- platform: file
|
||||||
@@ -545,22 +546,36 @@ display:
|
|||||||
if (id(show_status)) {
|
if (id(show_status)) {
|
||||||
first_draw = true;
|
first_draw = true;
|
||||||
it.fill(id(face_bg));
|
it.fill(id(face_bg));
|
||||||
it.image(160, 45, id(esphome_logo), ImageAlign::CENTER);
|
it.image(160, 46, id(esphome_logo), ImageAlign::CENTER);
|
||||||
it.print(160, 95, id(font_title), id(face_fg), TextAlign::CENTER, "${name}");
|
it.print(160, 95, id(font_title), id(face_fg), TextAlign::CENTER, "${name}");
|
||||||
|
|
||||||
if (wifi_ok) {
|
if (wifi_ok) {
|
||||||
it.printf(20, 135, id(font_status), id(status_ok), "WiFi: %s (%d dBm)",
|
it.printf(20, 114, id(font_status), id(status_ok), "WiFi: connected (%d dBm)", rssi);
|
||||||
id(ip_address_sensor).state.c_str(), rssi);
|
// one global IPv6 (skip link-local) and one IPv4 address
|
||||||
|
int y = 133;
|
||||||
|
char buf[network::IP_ADDRESS_BUFFER_SIZE];
|
||||||
|
for (bool v6 : {true, false}) {
|
||||||
|
for (auto &ip : wifi::global_wifi_component->get_ip_addresses()) {
|
||||||
|
if (!ip.is_set() || ip.is_ip6() != v6)
|
||||||
|
continue;
|
||||||
|
ip.str_to(buf);
|
||||||
|
if (v6 && strncmp(buf, "fe80", 4) == 0)
|
||||||
|
continue;
|
||||||
|
it.print(30, y, id(font_status), id(status_dim), buf);
|
||||||
|
y += 19;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
it.print(20, 135, id(font_status), id(status_wait), "WiFi: connecting...");
|
it.print(20, 114, id(font_status), id(status_wait), "WiFi: connecting...");
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool api_ok = api::global_api_server->is_connected();
|
const bool api_ok = api::global_api_server->is_connected();
|
||||||
it.print(20, 162, id(font_status), api_ok ? id(status_ok) : id(status_wait),
|
it.print(20, 172, id(font_status), api_ok ? id(status_ok) : id(status_wait),
|
||||||
api_ok ? "Home Assistant: connected" : "Home Assistant: waiting...");
|
api_ok ? "Home Assistant: connected" : "Home Assistant: waiting...");
|
||||||
|
|
||||||
if (batt >= 0) {
|
if (batt >= 0) {
|
||||||
it.printf(20, 189, id(font_status), batt <= 20 && !charging ? id(status_low) : id(status_dim),
|
it.printf(20, 191, id(font_status), batt <= 20 && !charging ? id(status_low) : id(status_dim),
|
||||||
"Battery: %d%%%s", batt, charging ? " (charging)" : "");
|
"Battery: %d%%%s", batt, charging ? " (charging)" : "");
|
||||||
}
|
}
|
||||||
it.printf(20, 212, id(font_status), id(status_dim), "ESPHome %s", ESPHOME_VERSION);
|
it.printf(20, 212, id(font_status), id(status_dim), "ESPHome %s", ESPHOME_VERSION);
|
||||||
@@ -883,4 +898,3 @@ time:
|
|||||||
id: rtc_time
|
id: rtc_time
|
||||||
i2c_id: i2c_bsp
|
i2c_id: i2c_bsp
|
||||||
update_interval: never
|
update_interval: never
|
||||||
- !include common/time/homeassistant.yaml
|
|
||||||
+231
-79
@@ -10,9 +10,10 @@ esphome:
|
|||||||
friendly_name: ${name}
|
friendly_name: ${name}
|
||||||
area: ${area}
|
area: ${area}
|
||||||
comment: ${comment}
|
comment: ${comment}
|
||||||
platformio_options:
|
on_boot:
|
||||||
board_build.flash_mode: dio
|
- priority: -100
|
||||||
|
then:
|
||||||
|
- lvgl.page.show: status_page
|
||||||
|
|
||||||
esp32:
|
esp32:
|
||||||
variant: esp32c3
|
variant: esp32c3
|
||||||
@@ -21,93 +22,244 @@ esp32:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
ipv6_only: !include common/ipv6_only.yaml
|
||||||
|
|
||||||
logger:
|
api:
|
||||||
logs:
|
# status page until Home Assistant connects (log viewers don't subscribe to states)
|
||||||
component: ERROR
|
on_client_connected:
|
||||||
|
- delay: 2s
|
||||||
# GPIO1 UART TX
|
- if:
|
||||||
# GPIO3 UART RX
|
condition:
|
||||||
# GPIO4 I2C SDA
|
api.connected:
|
||||||
# GPIO5 I2C SCL
|
state_subscription_only: true
|
||||||
# GPIO12 DISPLAY DIO
|
then:
|
||||||
# GPIO12 DISPLAY CLOCK
|
- lvgl.page.show: main_page
|
||||||
# GPIO14 BUZZER
|
on_client_disconnected:
|
||||||
# GPIO16 RGB
|
- if:
|
||||||
|
condition:
|
||||||
|
not:
|
||||||
|
api.connected:
|
||||||
|
state_subscription_only: true
|
||||||
|
then:
|
||||||
|
- lvgl.page.show: status_page
|
||||||
|
|
||||||
spi:
|
spi:
|
||||||
clk_pin: 4
|
clk_pin: 4
|
||||||
mosi_pin: 5
|
mosi_pin: 5
|
||||||
miso_pin: 6
|
miso_pin: 6
|
||||||
|
|
||||||
color:
|
|
||||||
- id: my_green
|
|
||||||
hex: 00ff00
|
|
||||||
- id: my_magenta
|
|
||||||
hex: ff00ff
|
|
||||||
- id: my_red
|
|
||||||
hex: ff0000
|
|
||||||
- id: my_white
|
|
||||||
hex: ffffff
|
|
||||||
|
|
||||||
font:
|
|
||||||
- file: "gfonts://Roboto"
|
|
||||||
id: font_default
|
|
||||||
size: 20
|
|
||||||
- file: "gfonts://B612"
|
|
||||||
id: b612_24
|
|
||||||
size: 24
|
|
||||||
- file: "gfonts://B612"
|
|
||||||
id: b612_16
|
|
||||||
size: 16
|
|
||||||
- file: "gfonts://B612"
|
|
||||||
id: b612_12
|
|
||||||
size: 12
|
|
||||||
- file: "gfonts://B612 Mono"
|
|
||||||
id: b612_24_mono
|
|
||||||
size: 24
|
|
||||||
- file: "gfonts://B612 Mono"
|
|
||||||
id: b612_16_mono
|
|
||||||
size: 16
|
|
||||||
- file: "gfonts://B612 Mono"
|
|
||||||
id: b612_12_mono
|
|
||||||
size: 12
|
|
||||||
- file: "gfonts://Roboto"
|
|
||||||
id: roboto_32
|
|
||||||
size: 32
|
|
||||||
- file: "gfonts://Roboto"
|
|
||||||
id: roboto_24
|
|
||||||
size: 24
|
|
||||||
- file: "gfonts://Roboto"
|
|
||||||
id: roboto_16
|
|
||||||
size: 16
|
|
||||||
- file: "gfonts://Roboto"
|
|
||||||
id: roboto_12
|
|
||||||
size: 12
|
|
||||||
- file: "gfonts://Roboto Mono"
|
|
||||||
id: roboto_32_mono
|
|
||||||
size: 32
|
|
||||||
- file: "gfonts://Roboto Mono"
|
|
||||||
id: roboto_24_mono
|
|
||||||
size: 24
|
|
||||||
- file: "gfonts://Roboto Mono"
|
|
||||||
id: roboto_16_mono
|
|
||||||
size: 16
|
|
||||||
- file: "gfonts://Roboto Mono"
|
|
||||||
id: roboto_12_mono
|
|
||||||
size: 12
|
|
||||||
|
|
||||||
|
|
||||||
display:
|
display:
|
||||||
- platform: mipi_spi
|
- platform: mipi_spi
|
||||||
|
id: main_display
|
||||||
model: ILI9341
|
model: ILI9341
|
||||||
cs_pin: 7
|
cs_pin: 7
|
||||||
dc_pin: 8
|
dc_pin: 8
|
||||||
reset_pin: 10
|
reset_pin: 10
|
||||||
invert_colors: false
|
invert_colors: false
|
||||||
lambda: |-
|
data_rate: 40MHz
|
||||||
it.fill(Color::BLACK);
|
update_interval: never
|
||||||
it.print(0, 0, id(b612_12), id(my_white), TextAlign::TOP_LEFT, "HELLO WORLD!");
|
auto_clear_enabled: false
|
||||||
it.print(0, 14, id(b612_12), id(my_green), TextAlign::TOP_LEFT, "HELLO WORLD!");
|
|
||||||
it.print(0, 28, id(b612_12), id(my_magenta), TextAlign::TOP_LEFT, "HELLO WORLD!");
|
touchscreen:
|
||||||
it.print(0, 42, id(b612_12), id(my_red), TextAlign::TOP_LEFT, "HELLO WORLD!");
|
- platform: xpt2046
|
||||||
|
display: main_display
|
||||||
|
cs_pin: 0
|
||||||
|
interrupt_pin: 1
|
||||||
|
transform:
|
||||||
|
mirror_x: true
|
||||||
|
calibration:
|
||||||
|
x_min: 330
|
||||||
|
x_max: 3620
|
||||||
|
y_min: 230
|
||||||
|
y_max: 3680
|
||||||
|
|
||||||
|
script:
|
||||||
|
- id: update_status
|
||||||
|
then:
|
||||||
|
- lambda: |-
|
||||||
|
// same colors as the other status screens: ok green, waiting orange, details gray
|
||||||
|
static const uint32_t OK = 0x4DD966, WAIT = 0xFFB300;
|
||||||
|
auto set = [](lv_obj_t *label, const std::string &text, uint32_t color) {
|
||||||
|
lv_label_set_text(label, text.c_str());
|
||||||
|
lv_obj_set_style_text_color(label, lv_color_hex(color), LV_PART_MAIN);
|
||||||
|
};
|
||||||
|
|
||||||
|
const bool wifi_ok = wifi::global_wifi_component->is_connected();
|
||||||
|
set(id(status_wifi),
|
||||||
|
wifi_ok ? str_sprintf("WiFi: connected (%d dBm)", wifi::global_wifi_component->wifi_rssi())
|
||||||
|
: std::string("WiFi: connecting..."),
|
||||||
|
wifi_ok ? OK : WAIT);
|
||||||
|
|
||||||
|
// IPv6 first (skip link-local), then IPv4
|
||||||
|
std::string ips;
|
||||||
|
if (wifi_ok) {
|
||||||
|
char buf[network::IP_ADDRESS_BUFFER_SIZE];
|
||||||
|
for (bool v6 : {true, false}) {
|
||||||
|
for (auto &ip : wifi::global_wifi_component->get_ip_addresses()) {
|
||||||
|
if (!ip.is_set() || ip.is_ip6() != v6)
|
||||||
|
continue;
|
||||||
|
ip.str_to(buf);
|
||||||
|
if (v6 && strncmp(buf, "fe80", 4) == 0)
|
||||||
|
continue;
|
||||||
|
if (!ips.empty())
|
||||||
|
ips += "\n";
|
||||||
|
ips += buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lv_label_set_text(id(status_ips), ips.c_str());
|
||||||
|
if (ips.empty())
|
||||||
|
lv_obj_add_flag(id(status_ips), LV_OBJ_FLAG_HIDDEN);
|
||||||
|
else
|
||||||
|
lv_obj_remove_flag(id(status_ips), LV_OBJ_FLAG_HIDDEN);
|
||||||
|
|
||||||
|
const bool api_ok = api::global_api_server->is_connected();
|
||||||
|
set(id(status_ha), api_ok ? "Home Assistant: connected" : "Home Assistant: waiting...",
|
||||||
|
api_ok ? OK : WAIT);
|
||||||
|
|
||||||
|
const uint32_t up = millis() / 1000;
|
||||||
|
lv_label_set_text(id(status_footer),
|
||||||
|
str_sprintf("ESPHome %s\nup %ud %02u:%02u:%02u", ESPHOME_VERSION, up / 86400,
|
||||||
|
(up / 3600) % 24, (up / 60) % 60, up % 60).c_str());
|
||||||
|
interval:
|
||||||
|
- interval: 1s
|
||||||
|
then:
|
||||||
|
- lvgl.label.update:
|
||||||
|
id: clock_label
|
||||||
|
text: !lambda |-
|
||||||
|
auto now = id(ha_time).now();
|
||||||
|
if (!now.is_valid())
|
||||||
|
return std::string("--:--");
|
||||||
|
return now.strftime("%H:%M");
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lvgl.page.is_showing: status_page
|
||||||
|
then:
|
||||||
|
- script.execute: update_status
|
||||||
|
|
||||||
|
image:
|
||||||
|
- platform: file
|
||||||
|
file: assets/images/made-for-esphome-white-on-black.svg
|
||||||
|
id: esphome_logo
|
||||||
|
resize: 220x65
|
||||||
|
type: RGB565
|
||||||
|
transparency: alpha_channel
|
||||||
|
|
||||||
|
font:
|
||||||
|
- file: "gfonts://Roboto@500"
|
||||||
|
id: font_clock
|
||||||
|
size: 80
|
||||||
|
glyphs: "0123456789:-"
|
||||||
|
- file: "gfonts://Roboto"
|
||||||
|
id: font_title
|
||||||
|
size: 24
|
||||||
|
- file: "gfonts://Roboto"
|
||||||
|
id: font_status
|
||||||
|
size: 14
|
||||||
|
|
||||||
|
lvgl:
|
||||||
|
log_level: WARN
|
||||||
|
color_depth: 16
|
||||||
|
bg_color: 0x000000
|
||||||
|
text_color: 0xFFFFFF
|
||||||
|
theme:
|
||||||
|
button:
|
||||||
|
bg_color: 0x2B2F36
|
||||||
|
radius: 12
|
||||||
|
checked:
|
||||||
|
bg_color: 0xE08A00
|
||||||
|
buttonmatrix:
|
||||||
|
bg_opa: TRANSP
|
||||||
|
border_width: 0
|
||||||
|
items:
|
||||||
|
bg_color: 0x2B2F36
|
||||||
|
text_color: 0xFFFFFF
|
||||||
|
radius: 12
|
||||||
|
checked:
|
||||||
|
bg_color: 0xE08A00
|
||||||
|
top_layer:
|
||||||
|
widgets:
|
||||||
|
# same navigation bar as on the other LVGL displays
|
||||||
|
- buttonmatrix:
|
||||||
|
id: nav_bar
|
||||||
|
align: bottom_mid
|
||||||
|
width: 100%
|
||||||
|
height: 48
|
||||||
|
pad_all: 4
|
||||||
|
pad_column: 6
|
||||||
|
items:
|
||||||
|
text_font: montserrat_20
|
||||||
|
rows:
|
||||||
|
- buttons:
|
||||||
|
- id: page_prev
|
||||||
|
text: ""
|
||||||
|
on_press:
|
||||||
|
then:
|
||||||
|
lvgl.page.previous:
|
||||||
|
- id: page_home
|
||||||
|
text: ""
|
||||||
|
on_press:
|
||||||
|
then:
|
||||||
|
lvgl.page.show: main_page
|
||||||
|
- id: page_next
|
||||||
|
text: ""
|
||||||
|
on_press:
|
||||||
|
then:
|
||||||
|
lvgl.page.next:
|
||||||
|
pages:
|
||||||
|
# 1: clock
|
||||||
|
- id: main_page
|
||||||
|
widgets:
|
||||||
|
- label:
|
||||||
|
id: clock_label
|
||||||
|
align: CENTER
|
||||||
|
y: -24
|
||||||
|
text_font: font_clock
|
||||||
|
text: "--:--"
|
||||||
|
|
||||||
|
# 2: status (shown until Home Assistant connects)
|
||||||
|
- id: status_page
|
||||||
|
on_load:
|
||||||
|
- script.execute: update_status
|
||||||
|
widgets:
|
||||||
|
- image:
|
||||||
|
src: esphome_logo
|
||||||
|
align: TOP_MID
|
||||||
|
y: 8
|
||||||
|
- label:
|
||||||
|
align: TOP_MID
|
||||||
|
y: 78
|
||||||
|
text_font: font_title
|
||||||
|
text: "${name}"
|
||||||
|
- obj:
|
||||||
|
x: 8
|
||||||
|
y: 116
|
||||||
|
width: 224
|
||||||
|
height: SIZE_CONTENT
|
||||||
|
bg_opa: TRANSP
|
||||||
|
border_width: 0
|
||||||
|
pad_all: 0
|
||||||
|
scrollbar_mode: "OFF"
|
||||||
|
layout:
|
||||||
|
type: flex
|
||||||
|
flex_flow: column
|
||||||
|
pad_row: 6
|
||||||
|
widgets:
|
||||||
|
- label:
|
||||||
|
id: status_wifi
|
||||||
|
text_font: font_status
|
||||||
|
text: ""
|
||||||
|
- label:
|
||||||
|
id: status_ips
|
||||||
|
text_font: font_status
|
||||||
|
text_color: 0x8C8C8C
|
||||||
|
text: ""
|
||||||
|
- label:
|
||||||
|
id: status_ha
|
||||||
|
text_font: font_status
|
||||||
|
text: ""
|
||||||
|
- label:
|
||||||
|
id: status_footer
|
||||||
|
text_font: font_status
|
||||||
|
text_color: 0x8C8C8C
|
||||||
|
text: ""
|
||||||
+1
-2
@@ -34,6 +34,7 @@ esp32:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
ipv6_only: !include common/ipv6_only.yaml
|
||||||
|
|
||||||
api:
|
api:
|
||||||
actions:
|
actions:
|
||||||
@@ -358,5 +359,3 @@ valve:
|
|||||||
close_action:
|
close_action:
|
||||||
- switch.turn_off: irrigation_relay_8
|
- switch.turn_off: irrigation_relay_8
|
||||||
|
|
||||||
time:
|
|
||||||
- !include common/time/homeassistant.yaml
|
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
wifi_ssid: "ssid"
|
wifi_ssid: "ssid"
|
||||||
wifi_password: "password"
|
wifi_password: "password"
|
||||||
|
|
||||||
|
wifi_ssid_ipv6: "ssid"
|
||||||
|
wifi_password_ipv6: "password"
|
||||||
|
|
||||||
api_password: "password"
|
api_password: "password"
|
||||||
api_key: "oSsCFxRUqIJLpT98cQwSwNxYoC7zbUh0YBHczAWRyBI="
|
api_key: "oSsCFxRUqIJLpT98cQwSwNxYoC7zbUh0YBHczAWRyBI="
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user