mirror of
https://github.com/randybb/esphome-configs.git
synced 2026-10-14 16:35:12 +02:00
Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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: 1d
|
||||||
|
|
||||||
|
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,176 @@
|
|||||||
|
#pragma once
|
||||||
|
// Hourly weather forecast for mcu-m5paper: parsed from the weather.get_forecasts
|
||||||
|
// response and kept in the on-board FM24C02 EEPROM (256 B, I2C 0x50), so it
|
||||||
|
// survives the power-off between RTC wake-ups.
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "esphome/components/i2c/i2c_bus.h"
|
||||||
|
#include "esphome/components/json/json_util.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
|
||||||
|
namespace m5paper_weather {
|
||||||
|
|
||||||
|
static constexpr uint8_t EEPROM_ADDR = 0x50;
|
||||||
|
static constexpr uint8_t EEPROM_PAGE = 8;
|
||||||
|
static constexpr uint8_t MAGIC = 0xA5;
|
||||||
|
static constexpr uint8_t VERSION = 1;
|
||||||
|
static constexpr uint8_t MAX_HOURS = 48;
|
||||||
|
// magic, version, fetched_at (u32), first_ts (u32), count
|
||||||
|
static constexpr size_t HEADER = 11;
|
||||||
|
static constexpr size_t MAX_SIZE = HEADER + MAX_HOURS * 4;
|
||||||
|
static_assert(MAX_SIZE <= 256, "forecast does not fit into the EEPROM");
|
||||||
|
|
||||||
|
static constexpr uint8_t NO_VALUE = 0xFF;
|
||||||
|
static constexpr int8_t NO_TEMP = INT8_MIN;
|
||||||
|
|
||||||
|
struct Hour {
|
||||||
|
uint8_t cond; // index into CONDITIONS, NO_VALUE if unknown
|
||||||
|
int8_t temp2; // temperature in 0.5 °C, NO_TEMP if unknown
|
||||||
|
uint8_t wind; // km/h, NO_VALUE if unknown
|
||||||
|
uint8_t pop; // precipitation probability %, NO_VALUE if unknown
|
||||||
|
};
|
||||||
|
static_assert(sizeof(Hour) == 4, "Hour must stay 4 bytes");
|
||||||
|
|
||||||
|
struct Forecast {
|
||||||
|
uint32_t fetched_at{0}; // UTC epoch of the download
|
||||||
|
uint32_t first_ts{0}; // UTC epoch of hours[0]; entries are 1 h apart
|
||||||
|
uint8_t count{0};
|
||||||
|
Hour hours[MAX_HOURS]{};
|
||||||
|
|
||||||
|
bool valid() const { return this->count > 0; }
|
||||||
|
// hour covering the given UTC epoch, nullptr if outside the forecast
|
||||||
|
const Hour *at(uint32_t ts) const {
|
||||||
|
if (!this->valid() || ts < this->first_ts)
|
||||||
|
return nullptr;
|
||||||
|
uint32_t idx = (ts - this->first_ts) / 3600;
|
||||||
|
return idx < this->count ? &this->hours[idx] : nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// the forecast in RAM, loaded from the EEPROM at boot
|
||||||
|
inline Forecast &forecast() {
|
||||||
|
static Forecast fc;
|
||||||
|
return fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Home Assistant weather conditions; the index is the stored condition code
|
||||||
|
static const char *const CONDITIONS[] = {
|
||||||
|
"clear-night", "cloudy", "exceptional", "fog", "hail", "lightning", "lightning-rainy", "partlycloudy",
|
||||||
|
"pouring", "rainy", "snowy", "snowy-rainy", "sunny", "windy", "windy-variant",
|
||||||
|
};
|
||||||
|
static const char *const CONDITION_NAMES[] = {
|
||||||
|
"Jasno", "Oblačno", "Extrémne počasie", "Hmla", "Krupobitie", "Búrka", "Búrka s dažďom", "Polooblačno",
|
||||||
|
"Lejak", "Dážď", "Sneženie", "Dážď so snehom", "Slnečno", "Veterno", "Veterno, oblačno",
|
||||||
|
};
|
||||||
|
static constexpr uint8_t NUM_CONDITIONS = sizeof(CONDITIONS) / sizeof(CONDITIONS[0]);
|
||||||
|
|
||||||
|
inline uint8_t condition_code(const char *name) {
|
||||||
|
for (uint8_t i = 0; i < NUM_CONDITIONS; i++) {
|
||||||
|
if (strcmp(name, CONDITIONS[i]) == 0)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return NO_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const char *condition_name(uint8_t code) { return code < NUM_CONDITIONS ? CONDITION_NAMES[code] : "?"; }
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "2026-09-27T16:00:00+00:00" -> UTC epoch, 0 on error
|
||||||
|
inline uint32_t parse_iso8601(const char *s) {
|
||||||
|
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 < 5)
|
||||||
|
return 0;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
return ts > 0 ? uint32_t(ts) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the weather.get_forecasts response for the given entity;
|
||||||
|
// Home Assistant wraps the action response as {"response": {...}}
|
||||||
|
inline bool parse(ArduinoJson::JsonObjectConst response, const char *entity, uint32_t now, Forecast &fc) {
|
||||||
|
ArduinoJson::JsonObjectConst root = response["response"].is<ArduinoJson::JsonObjectConst>()
|
||||||
|
? response["response"].as<ArduinoJson::JsonObjectConst>()
|
||||||
|
: response;
|
||||||
|
ArduinoJson::JsonArrayConst items = root[entity]["forecast"];
|
||||||
|
if (items.isNull() || items.size() == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Forecast out;
|
||||||
|
for (ArduinoJson::JsonObjectConst item : items) {
|
||||||
|
if (out.count >= MAX_HOURS)
|
||||||
|
break;
|
||||||
|
if (out.count == 0) {
|
||||||
|
out.first_ts = parse_iso8601(item["datetime"] | "");
|
||||||
|
if (out.first_ts == 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Hour &hour = out.hours[out.count++];
|
||||||
|
hour.cond = condition_code(item["condition"] | "");
|
||||||
|
float temp = item["temperature"] | NAN;
|
||||||
|
hour.temp2 = std::isnan(temp) ? NO_TEMP : int8_t(std::max(-127L, std::min(127L, lroundf(temp * 2))));
|
||||||
|
float wind = item["wind_speed"] | NAN;
|
||||||
|
hour.wind = std::isnan(wind) ? NO_VALUE : uint8_t(std::max(0L, std::min(254L, lroundf(wind))));
|
||||||
|
float pop = item["precipitation_probability"] | NAN;
|
||||||
|
hour.pop = std::isnan(pop) ? NO_VALUE : uint8_t(std::max(0L, std::min(100L, lroundf(pop))));
|
||||||
|
}
|
||||||
|
out.fetched_at = now;
|
||||||
|
fc = out;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool load(esphome::i2c::I2CBus *bus, Forecast &fc) {
|
||||||
|
uint8_t buf[MAX_SIZE];
|
||||||
|
const uint8_t start = 0;
|
||||||
|
fc.count = 0;
|
||||||
|
if (bus->write_readv(EEPROM_ADDR, &start, 1, buf, sizeof(buf)) != esphome::i2c::ERROR_OK)
|
||||||
|
return false;
|
||||||
|
if (buf[0] != MAGIC || buf[1] != VERSION)
|
||||||
|
return false;
|
||||||
|
memcpy(&fc.fetched_at, buf + 2, 4);
|
||||||
|
memcpy(&fc.first_ts, buf + 6, 4);
|
||||||
|
fc.count = std::min(buf[10], MAX_HOURS);
|
||||||
|
memcpy(fc.hours, buf + HEADER, fc.count * sizeof(Hour));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool save(esphome::i2c::I2CBus *bus, const Forecast &fc) {
|
||||||
|
uint8_t buf[MAX_SIZE];
|
||||||
|
buf[0] = MAGIC;
|
||||||
|
buf[1] = VERSION;
|
||||||
|
memcpy(buf + 2, &fc.fetched_at, 4);
|
||||||
|
memcpy(buf + 6, &fc.first_ts, 4);
|
||||||
|
buf[10] = fc.count;
|
||||||
|
memcpy(buf + HEADER, fc.hours, fc.count * sizeof(Hour));
|
||||||
|
const size_t len = HEADER + fc.count * sizeof(Hour);
|
||||||
|
|
||||||
|
// page writes must not cross an 8-byte page boundary; 5 ms write cycle per page
|
||||||
|
for (size_t off = 0; off < len; off += EEPROM_PAGE) {
|
||||||
|
uint8_t page[EEPROM_PAGE + 1];
|
||||||
|
const size_t n = std::min<size_t>(EEPROM_PAGE, len - off);
|
||||||
|
page[0] = uint8_t(off);
|
||||||
|
memcpy(page + 1, buf + off, n);
|
||||||
|
if (bus->write(EEPROM_ADDR, page, n + 1) != esphome::i2c::ERROR_OK)
|
||||||
|
return false;
|
||||||
|
esphome::delay(6);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace m5paper_weather
|
||||||
+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
|
||||||
|
|||||||
@@ -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
|
|
||||||
+564
-77
@@ -4,94 +4,370 @@ substitutions:
|
|||||||
name: M5Paper
|
name: M5Paper
|
||||||
area: DEV
|
area: DEV
|
||||||
comment: M5Paper
|
comment: M5Paper
|
||||||
|
weather_entity: weather.openweathermap
|
||||||
|
|
||||||
|
# Power modes
|
||||||
|
# - USB: always on, WiFi + API, forecast refreshed every hour and redrawn every 15 min
|
||||||
|
# - battery: RTC wakes the board every 15 min (off 1:00-5:00), it draws the forecast
|
||||||
|
# from EEPROM and powers itself off; WiFi only to refresh the forecast every 6 h
|
||||||
|
# Screens: status (after power-on, until Home Assistant connects) and forecast
|
||||||
|
# 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_weather.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_weather::load(id(i2c_main), m5paper_weather::forecast());
|
||||||
|
ESP_LOGI("m5paper", "Wake-up: %s, forecast: %u h", id(rtc_wake) ? "RTC" : "power-on", m5paper_weather::forecast().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
|
||||||
|
|
||||||
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: forecast screen regardless of the connection state
|
||||||
|
- id: weather_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(weather_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(weather_page);
|
||||||
|
then:
|
||||||
|
- component.update: m5paper_display
|
||||||
|
# outside the battery cycle: refresh a forecast older than an hour
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
and:
|
||||||
|
- lambda: |-
|
||||||
|
const auto &fc = m5paper_weather::forecast();
|
||||||
|
const auto now = id(rtc_time).now();
|
||||||
|
return !id(weather_page) && (!fc.valid() || !now.is_valid() || now.timestamp - fc.fetched_at >= 3600);
|
||||||
|
- api.connected:
|
||||||
|
state_subscription_only: true
|
||||||
|
then:
|
||||||
|
- script.execute: fetch_forecast
|
||||||
|
on_client_disconnected:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
not:
|
||||||
|
api.connected:
|
||||||
|
state_subscription_only: true
|
||||||
|
then:
|
||||||
|
- lambda: id(show_status) = true;
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return !id(weather_page);
|
||||||
|
then:
|
||||||
|
- component.update: m5paper_display
|
||||||
|
|
||||||
|
# I2C devices (i2c_main)
|
||||||
# 0x44 SHT30
|
# 0x44 SHT30
|
||||||
# 0x50
|
# 0x50 FM24C02 EEPROM (forecast storage, see include/m5paper_weather.h)
|
||||||
# 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 forecast if it is old, draw it, power off
|
||||||
|
- id: battery_cycle
|
||||||
|
mode: single
|
||||||
|
then:
|
||||||
|
- lambda: id(weather_page) = true;
|
||||||
|
- wait_until:
|
||||||
|
condition:
|
||||||
|
lambda: return id(rtc_time).now().is_valid();
|
||||||
|
timeout: 10s
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: |-
|
||||||
|
const auto &fc = m5paper_weather::forecast();
|
||||||
|
const auto now = id(rtc_time).now();
|
||||||
|
return !fc.valid() || !now.is_valid() || now.timestamp - fc.fetched_at >= 6 * 3600 - 600;
|
||||||
|
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_forecast
|
||||||
|
- wait_until:
|
||||||
|
condition:
|
||||||
|
lambda: return id(fetch_done);
|
||||||
|
timeout: 15s
|
||||||
|
- wait_until:
|
||||||
|
condition:
|
||||||
|
lambda: return !std::isnan(id(current_temperature).state);
|
||||||
|
timeout: 5s
|
||||||
|
- component.update: m5paper_display
|
||||||
|
# let the e-paper refresh finish before cutting power
|
||||||
|
- delay: 4s
|
||||||
|
- script.execute: try_sleep
|
||||||
|
|
||||||
|
- id: fetch_forecast
|
||||||
|
mode: single
|
||||||
|
then:
|
||||||
|
- lambda: id(fetch_done) = false;
|
||||||
|
- homeassistant.action:
|
||||||
|
action: weather.get_forecasts
|
||||||
|
data:
|
||||||
|
entity_id: ${weather_entity}
|
||||||
|
type: hourly
|
||||||
|
capture_response: true
|
||||||
|
on_success:
|
||||||
|
- lambda: |-
|
||||||
|
auto &fc = m5paper_weather::forecast();
|
||||||
|
if (m5paper_weather::parse(response, "${weather_entity}", id(rtc_time).now().timestamp, fc)) {
|
||||||
|
const bool saved = m5paper_weather::save(id(i2c_main), fc);
|
||||||
|
ESP_LOGI("m5paper", "Forecast: %u h%s", fc.count, saved ? "" : " (EEPROM write failed)");
|
||||||
|
} else {
|
||||||
|
ESP_LOGW("m5paper", "Forecast: unexpected response");
|
||||||
|
}
|
||||||
|
id(fetch_done) = true;
|
||||||
|
# the battery cycle draws on its own
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return !id(weather_page);
|
||||||
|
then:
|
||||||
|
- component.update: m5paper_display
|
||||||
|
on_error:
|
||||||
|
- lambda: |-
|
||||||
|
ESP_LOGW("m5paper", "Forecast: %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 quarter hour, 1:00-5:00 in one go
|
||||||
|
int duration = 900;
|
||||||
|
const auto now = id(rtc_time).now();
|
||||||
|
if (now.is_valid()) {
|
||||||
|
const int sod = now.hour * 3600 + now.minute * 60 + now.second;
|
||||||
|
int next = (sod / 900 + 1) * 900;
|
||||||
|
if (next - sod < 90) // the 1/60 Hz RTC timer may wake us up to a minute early
|
||||||
|
next += 900;
|
||||||
|
const int next_hour = (next / 3600) % 24;
|
||||||
|
if (next_hour >= 1 && next_hour < 5)
|
||||||
|
next = (next / 86400) * 86400 + 5 * 3600;
|
||||||
|
duration = next - sod;
|
||||||
|
}
|
||||||
|
if (duration > 255) // longer timers count whole minutes
|
||||||
|
duration = (duration + 59) / 60 * 60;
|
||||||
|
id(sleep_s) = std::min(duration, 255 * 60);
|
||||||
|
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(weather_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(weather_page);
|
||||||
|
then:
|
||||||
|
- component.update: m5paper_display
|
||||||
|
on_time:
|
||||||
|
- seconds: 0
|
||||||
|
minutes: /15
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: return !id(show_status) && !id(weather_page);
|
||||||
|
then:
|
||||||
|
- component.update: m5paper_display
|
||||||
|
# USB mode: keep the stored forecast fresh for battery mode
|
||||||
|
- seconds: 0
|
||||||
|
minutes: 5
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
and:
|
||||||
|
- lambda: return id(usb_mode);
|
||||||
|
- api.connected:
|
||||||
|
state_subscription_only: true
|
||||||
|
then:
|
||||||
|
- script.execute: fetch_forecast
|
||||||
|
- 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 +379,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 Forecast
|
||||||
id: roboto_24
|
entity_category: config
|
||||||
size: 24
|
on_press:
|
||||||
- file: "gfonts://Roboto"
|
- script.execute: fetch_forecast
|
||||||
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 +400,252 @@ 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_large: &icon_large {platform: file, type: BINARY, transparency: chroma_key, resize: 150x150}
|
||||||
# interrupt_pin: 36
|
icon_small: &icon_small {platform: file, type: BINARY, transparency: chroma_key, resize: 44x44}
|
||||||
|
|
||||||
|
image:
|
||||||
|
- platform: file
|
||||||
|
file: assets/images/made-for-esphome-white-on-black.svg
|
||||||
|
id: esphome_logo
|
||||||
|
resize: 460x136
|
||||||
|
type: GRAYSCALE
|
||||||
|
transparency: alpha_channel
|
||||||
|
# weather icons, same order as m5paper_weather::CONDITIONS
|
||||||
|
- {<<: *icon_large, file: "mdi:weather-night", id: wl_clear_night}
|
||||||
|
- {<<: *icon_large, file: "mdi:weather-cloudy", id: wl_cloudy}
|
||||||
|
- {<<: *icon_large, file: "mdi:alert-circle-outline", id: wl_exceptional}
|
||||||
|
- {<<: *icon_large, file: "mdi:weather-fog", id: wl_fog}
|
||||||
|
- {<<: *icon_large, file: "mdi:weather-hail", id: wl_hail}
|
||||||
|
- {<<: *icon_large, file: "mdi:weather-lightning", id: wl_lightning}
|
||||||
|
- {<<: *icon_large, file: "mdi:weather-lightning-rainy", id: wl_lightning_rainy}
|
||||||
|
- {<<: *icon_large, file: "mdi:weather-partly-cloudy", id: wl_partlycloudy}
|
||||||
|
- {<<: *icon_large, file: "mdi:weather-pouring", id: wl_pouring}
|
||||||
|
- {<<: *icon_large, file: "mdi:weather-rainy", id: wl_rainy}
|
||||||
|
- {<<: *icon_large, file: "mdi:weather-snowy", id: wl_snowy}
|
||||||
|
- {<<: *icon_large, file: "mdi:weather-snowy-rainy", id: wl_snowy_rainy}
|
||||||
|
- {<<: *icon_large, file: "mdi:weather-sunny", id: wl_sunny}
|
||||||
|
- {<<: *icon_large, file: "mdi:weather-windy", id: wl_windy}
|
||||||
|
- {<<: *icon_large, file: "mdi:weather-windy-variant", id: wl_windy_variant}
|
||||||
|
- {<<: *icon_small, file: "mdi:weather-night", id: ws_clear_night}
|
||||||
|
- {<<: *icon_small, file: "mdi:weather-cloudy", id: ws_cloudy}
|
||||||
|
- {<<: *icon_small, file: "mdi:alert-circle-outline", id: ws_exceptional}
|
||||||
|
- {<<: *icon_small, file: "mdi:weather-fog", id: ws_fog}
|
||||||
|
- {<<: *icon_small, file: "mdi:weather-hail", id: ws_hail}
|
||||||
|
- {<<: *icon_small, file: "mdi:weather-lightning", id: ws_lightning}
|
||||||
|
- {<<: *icon_small, file: "mdi:weather-lightning-rainy", id: ws_lightning_rainy}
|
||||||
|
- {<<: *icon_small, file: "mdi:weather-partly-cloudy", id: ws_partlycloudy}
|
||||||
|
- {<<: *icon_small, file: "mdi:weather-pouring", id: ws_pouring}
|
||||||
|
- {<<: *icon_small, file: "mdi:weather-rainy", id: ws_rainy}
|
||||||
|
- {<<: *icon_small, file: "mdi:weather-snowy", id: ws_snowy}
|
||||||
|
- {<<: *icon_small, file: "mdi:weather-snowy-rainy", id: ws_snowy_rainy}
|
||||||
|
- {<<: *icon_small, file: "mdi:weather-sunny", id: ws_sunny}
|
||||||
|
- {<<: *icon_small, file: "mdi:weather-windy", id: ws_windy}
|
||||||
|
- {<<: *icon_small, file: "mdi:weather-windy-variant", id: ws_windy_variant}
|
||||||
|
|
||||||
|
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_tiny
|
||||||
|
size: 18
|
||||||
|
glyphs: "0123456789% "
|
||||||
|
- file: "gfonts://Roboto@500"
|
||||||
|
id: font_date
|
||||||
|
size: 34
|
||||||
|
glyphs: *sk_glyphs
|
||||||
|
glyphsets: [GF_Latin_Kernel]
|
||||||
|
- file: "gfonts://Roboto@500"
|
||||||
|
id: font_hour
|
||||||
|
size: 28
|
||||||
|
glyphs: "0123456789-°"
|
||||||
|
- file: "gfonts://Roboto"
|
||||||
|
id: font_temp
|
||||||
|
size: 110
|
||||||
|
glyphs: "0123456789.,-°C "
|
||||||
|
|
||||||
|
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;
|
||||||
|
const float temp = id(current_temperature).state;
|
||||||
|
const float hum = id(current_humidity).state;
|
||||||
|
|
||||||
|
// status screen after power-on, until Home Assistant connects
|
||||||
|
if (id(show_status) && !id(weather_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), "Forecast: %u h stored", m5paper_weather::forecast().count);
|
||||||
|
it.printf(40, 900, id(font_small), id(ink), "ESPHome %s", ESPHOME_VERSION);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// forecast screen
|
||||||
|
using namespace m5paper_weather;
|
||||||
|
image::Image *large[] = {id(wl_clear_night), id(wl_cloudy), id(wl_exceptional), id(wl_fog),
|
||||||
|
id(wl_hail), id(wl_lightning), id(wl_lightning_rainy), id(wl_partlycloudy),
|
||||||
|
id(wl_pouring), id(wl_rainy), id(wl_snowy), id(wl_snowy_rainy),
|
||||||
|
id(wl_sunny), id(wl_windy), id(wl_windy_variant)};
|
||||||
|
image::Image *small[] = {id(ws_clear_night), id(ws_cloudy), id(ws_exceptional), id(ws_fog),
|
||||||
|
id(ws_hail), id(ws_lightning), id(ws_lightning_rainy), id(ws_partlycloudy),
|
||||||
|
id(ws_pouring), id(ws_rainy), id(ws_snowy), id(ws_snowy_rainy),
|
||||||
|
id(ws_sunny), id(ws_windy), id(ws_windy_variant)};
|
||||||
|
static const char *const DAYS[] = {"Nedeľa", "Pondelok", "Utorok", "Streda", "Štvrtok", "Piatok", "Sobota"};
|
||||||
|
|
||||||
|
const auto &fc = m5paper_weather::forecast();
|
||||||
|
const auto now = id(rtc_time).now();
|
||||||
|
|
||||||
|
// header: date, battery
|
||||||
|
if (now.is_valid())
|
||||||
|
it.printf(24, 16, id(font_date), id(ink), "%s %d. %d.", DAYS[(now.day_of_week + 6) % 7], 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);
|
||||||
|
|
||||||
|
const Hour *cur = now.is_valid() ? fc.at(now.timestamp) : nullptr;
|
||||||
|
if (cur == nullptr) {
|
||||||
|
it.print(cx, 230, id(font_status), id(ink), TextAlign::CENTER, "Predpoveď nie je k dispozícii");
|
||||||
|
} else {
|
||||||
|
// current hour
|
||||||
|
if (cur->cond < NUM_CONDITIONS)
|
||||||
|
it.image(24, 80, large[cur->cond], id(ink));
|
||||||
|
if (cur->temp2 != NO_TEMP)
|
||||||
|
it.printf(200, 70, id(font_temp), id(ink), "%.0f°", cur->temp2 / 2.0f);
|
||||||
|
it.print(204, 200, id(font_status), id(ink), condition_name(cur->cond));
|
||||||
|
if (cur->wind != NO_VALUE)
|
||||||
|
it.printf(204, 245, id(font_small), id(ink), "vietor %u km/h", cur->wind);
|
||||||
|
if (cur->pop != NO_VALUE)
|
||||||
|
it.printf(204, 280, id(font_small), id(ink), "zrážky %u %%", cur->pop);
|
||||||
|
|
||||||
|
// next 24 hours: 3 columns x 8 rows
|
||||||
|
const uint32_t first = (now.timestamp / 3600 + 1) * 3600;
|
||||||
|
const int top = 440, row_h = 60, col_w = w / 3;
|
||||||
|
for (int i = 0; i < 24; i++) {
|
||||||
|
const Hour *h = fc.at(first + i * 3600);
|
||||||
|
if (h == nullptr)
|
||||||
|
break;
|
||||||
|
const int x = (i / 8) * col_w + 12;
|
||||||
|
const int y = top + (i % 8) * row_h;
|
||||||
|
const auto t = ESPTime::from_epoch_local(first + i * 3600);
|
||||||
|
it.printf(x, y + 14, id(font_hour), id(ink), "%02d", t.hour);
|
||||||
|
if (h->cond < NUM_CONDITIONS)
|
||||||
|
it.image(x + 42, y + 8, small[h->cond], id(ink));
|
||||||
|
if (h->temp2 != NO_TEMP)
|
||||||
|
it.printf(x + 94, y + 6, id(font_hour), id(ink), "%.0f°", h->temp2 / 2.0f);
|
||||||
|
if (h->pop != NO_VALUE && h->pop >= 10)
|
||||||
|
it.printf(x + 94, y + 38, id(font_tiny), id(ink), "%u %%", h->pop);
|
||||||
|
}
|
||||||
|
for (int c = 1; c < 3; c++)
|
||||||
|
it.vertical_line(c * col_w, top, 8 * row_h, id(ink));
|
||||||
|
}
|
||||||
|
|
||||||
|
// indoor
|
||||||
|
it.horizontal_line(24, 340, w - 48, id(ink));
|
||||||
|
it.printf(cx, 380, id(font_status), id(ink), TextAlign::CENTER, "Doma %.1f °C · %.0f %%", temp, hum);
|
||||||
|
it.horizontal_line(24, 420, w - 48, id(ink));
|
||||||
|
|
||||||
|
// footer
|
||||||
|
if (fc.valid()) {
|
||||||
|
const auto f = ESPTime::from_epoch_local(fc.fetched_at);
|
||||||
|
it.printf(24, 944, id(font_small), id(ink), TextAlign::BOTTOM_LEFT, "predpoveď %d. %d. %02d:%02d",
|
||||||
|
f.day_of_month, f.month, f.hour, f.minute);
|
||||||
|
}
|
||||||
|
if (now.is_valid())
|
||||||
|
it.printf(w - 24, 944, id(font_small), id(ink), TextAlign::BOTTOM_RIGHT, "%02d:%02d", now.hour, now.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();
|
|
||||||
+5
-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
|
||||||
@@ -251,12 +169,6 @@ binary_sensor:
|
|||||||
else:
|
else:
|
||||||
- switch.toggle: relay_2
|
- switch.toggle: relay_2
|
||||||
|
|
||||||
output:
|
|
||||||
- platform: ledc
|
|
||||||
id: buzzer_out
|
|
||||||
pin:
|
|
||||||
number: 21
|
|
||||||
|
|
||||||
switch:
|
switch:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
name: Outside Light Back
|
name: Outside Light Back
|
||||||
@@ -271,47 +183,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:
|
||||||
|
|||||||
+23
-9
@@ -33,6 +33,7 @@ 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
|
||||||
@@ -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
|
|
||||||
@@ -21,6 +21,7 @@ esp32:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
common: !include common/common.yaml
|
common: !include common/common.yaml
|
||||||
|
ipv6_only: !include common/ipv6_only.yaml
|
||||||
|
|
||||||
logger:
|
logger:
|
||||||
logs:
|
logs:
|
||||||
@@ -49,6 +50,20 @@ color:
|
|||||||
hex: ff0000
|
hex: ff0000
|
||||||
- id: my_white
|
- id: my_white
|
||||||
hex: ffffff
|
hex: ffffff
|
||||||
|
- id: status_ok
|
||||||
|
hex: 4dd966
|
||||||
|
- id: status_wait
|
||||||
|
hex: ffb300
|
||||||
|
- id: status_dim
|
||||||
|
hex: 8c8c8c
|
||||||
|
|
||||||
|
image:
|
||||||
|
- platform: file
|
||||||
|
file: assets/images/made-for-esphome-white-on-black.svg
|
||||||
|
id: esphome_logo
|
||||||
|
resize: 220x65
|
||||||
|
type: RGB565
|
||||||
|
transparency: alpha_channel
|
||||||
|
|
||||||
font:
|
font:
|
||||||
- file: "gfonts://Roboto"
|
- file: "gfonts://Roboto"
|
||||||
@@ -107,6 +122,46 @@ display:
|
|||||||
invert_colors: false
|
invert_colors: false
|
||||||
lambda: |-
|
lambda: |-
|
||||||
it.fill(Color::BLACK);
|
it.fill(Color::BLACK);
|
||||||
|
|
||||||
|
// status screen until Home Assistant connects (240x320)
|
||||||
|
if (!api::global_api_server->is_connected_with_state_subscription()) {
|
||||||
|
const int cx = it.get_width() / 2;
|
||||||
|
it.image(cx, 48, id(esphome_logo), ImageAlign::CENTER);
|
||||||
|
it.print(cx, 100, id(roboto_24), id(my_white), TextAlign::CENTER, "${name}");
|
||||||
|
|
||||||
|
int y = 130;
|
||||||
|
if (wifi::global_wifi_component->is_connected()) {
|
||||||
|
it.printf(8, y, id(roboto_16), id(status_ok), "WiFi: connected (%d dBm)",
|
||||||
|
wifi::global_wifi_component->wifi_rssi());
|
||||||
|
y += 22;
|
||||||
|
// IPv6 first (skip link-local), then IPv4
|
||||||
|
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 > 200)
|
||||||
|
continue;
|
||||||
|
ip.str_to(buf);
|
||||||
|
if (v6 && strncmp(buf, "fe80", 4) == 0)
|
||||||
|
continue;
|
||||||
|
it.print(8, y, id(roboto_12), id(status_dim), buf);
|
||||||
|
y += 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
it.print(8, y, id(roboto_16), id(status_wait), "WiFi: connecting...");
|
||||||
|
y += 22;
|
||||||
|
}
|
||||||
|
y += 8;
|
||||||
|
it.print(8, y, id(roboto_16), api::global_api_server->is_connected() ? id(status_ok) : id(status_wait),
|
||||||
|
api::global_api_server->is_connected() ? "Home Assistant: connected" : "Home Assistant: waiting...");
|
||||||
|
|
||||||
|
it.printf(8, it.get_height() - 8, id(roboto_12), id(status_dim), TextAlign::BOTTOM_LEFT, "ESPHome %s",
|
||||||
|
ESPHOME_VERSION);
|
||||||
|
it.printf(it.get_width() - 8, it.get_height() - 8, id(roboto_12), id(status_dim), TextAlign::BOTTOM_RIGHT,
|
||||||
|
"up %us", (unsigned) (millis() / 1000));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
it.print(0, 0, id(b612_12), id(my_white), TextAlign::TOP_LEFT, "HELLO WORLD!");
|
it.print(0, 0, id(b612_12), id(my_white), TextAlign::TOP_LEFT, "HELLO WORLD!");
|
||||||
it.print(0, 14, id(b612_12), id(my_green), TextAlign::TOP_LEFT, "HELLO WORLD!");
|
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!");
|
it.print(0, 28, id(b612_12), id(my_magenta), TextAlign::TOP_LEFT, "HELLO WORLD!");
|
||||||
|
|||||||
+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
|
|
||||||
Reference in new issue
Block a user