Compare commits
4821 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
353868414a | ||
![]() |
c661d8f5b7 | ||
![]() |
02f14baad4 | ||
![]() |
e979c58c98 | ||
![]() |
b941654a68 | ||
![]() |
b0b3196e52 | ||
![]() |
7e87891701 | ||
![]() |
e76e9a3e1f | ||
![]() |
36cb1cad36 | ||
![]() |
22e2b6f3c5 | ||
![]() |
9c8f8c645e | ||
![]() |
e21a09cec9 | ||
![]() |
a0d1a8cbc4 | ||
![]() |
86393e0b1d | ||
![]() |
f328713710 | ||
![]() |
ed6efe4c9e | ||
![]() |
befff2f034 | ||
![]() |
630315180d | ||
![]() |
a70bfa0c89 | ||
![]() |
d2b7e474d6 | ||
![]() |
494b72c287 | ||
![]() |
229e7b940f | ||
![]() |
95dcb03f6d | ||
![]() |
a6d9a8220c | ||
![]() |
0ba80ce61e | ||
![]() |
7c23872e03 | ||
![]() |
507938be35 | ||
![]() |
8bee304e4d | ||
![]() |
be239993f4 | ||
![]() |
f396dac1cc | ||
![]() |
ebba1f78ab | ||
![]() |
c8c3fc2bcc | ||
![]() |
14c6c05e00 | ||
![]() |
3fbdd9635f | ||
![]() |
d7be9c9a66 | ||
![]() |
928c9fbacd | ||
![]() |
2b666ab9f7 | ||
![]() |
4ecc531998 | ||
![]() |
b908384ba2 | ||
![]() |
b27a4a94e4 | ||
![]() |
5e8073022b | ||
![]() |
9553425374 | ||
![]() |
2012317bc9 | ||
![]() |
18a118bb7a | ||
![]() |
e00789f838 | ||
![]() |
20563e6306 | ||
![]() |
580c2d8213 | ||
![]() |
c9b8789ea9 | ||
![]() |
4271588ecd | ||
![]() |
a5277ff4a0 | ||
![]() |
a66f5d660d | ||
![]() |
80061e8d76 | ||
![]() |
d07a057f04 | ||
![]() |
fa2949af7d | ||
![]() |
a4680b7216 | ||
![]() |
523eb910a6 | ||
![]() |
2f21de79f7 | ||
![]() |
092d402ff8 | ||
![]() |
12e8d6c4c6 | ||
![]() |
8c1486d577 | ||
![]() |
57e9199baa | ||
![]() |
f4aac0bd8d | ||
![]() |
664150811a | ||
![]() |
3cb4ae16ab | ||
![]() |
73fc9ea54d | ||
![]() |
dd80919ed6 | ||
![]() |
d7103cd75c | ||
![]() |
29beee367c | ||
![]() |
d22fe9227b | ||
![]() |
6ea9535463 | ||
![]() |
bbd1b730b1 | ||
![]() |
6e138e4f79 | ||
![]() |
d6453bb53d | ||
![]() |
9bfd34e6b2 | ||
![]() |
7b13771cfe | ||
![]() |
b70b222f80 | ||
![]() |
2ffc8dd228 | ||
![]() |
08d12f9ee6 | ||
![]() |
7ce96c190f | ||
![]() |
409b4b1963 | ||
![]() |
05cd7d753e | ||
![]() |
7be460e8b1 | ||
![]() |
6dfbd07e86 | ||
![]() |
7b56e53c47 | ||
![]() |
a8cde3289a | ||
![]() |
f4e7202dd0 | ||
![]() |
a2de3d33bf | ||
![]() |
46a3e3d353 | ||
![]() |
756e27fb52 | ||
![]() |
1d6e5a2464 | ||
![]() |
dc77428df4 | ||
![]() |
b293356cec | ||
![]() |
1dc7647bb9 | ||
![]() |
c3f6537a4d | ||
![]() |
70a3dc938a | ||
![]() |
1fd703aff9 | ||
![]() |
aa3fb7d165 | ||
![]() |
2b9ff3148c | ||
![]() |
7f24269511 | ||
![]() |
b34d65fce0 | ||
![]() |
778cecb512 | ||
![]() |
e7e0eb0f32 | ||
![]() |
a0c55c6406 | ||
![]() |
aba736cb96 | ||
![]() |
bdec873fed | ||
![]() |
2cc73660bf | ||
![]() |
f3de45c6ad | ||
![]() |
386e2c6306 | ||
![]() |
2473065b98 | ||
![]() |
ed91c54654 | ||
![]() |
4d53e0adde | ||
![]() |
35f87365c9 | ||
![]() |
8e7d6d5dad | ||
![]() |
95a10c692c | ||
![]() |
77d7082ffc | ||
![]() |
c43d09c8b1 | ||
![]() |
d92e60ee5f | ||
![]() |
b9ceacb43d | ||
![]() |
2fe809f15a | ||
![]() |
078a054dbd | ||
![]() |
c57be77039 | ||
![]() |
e6910f732f | ||
![]() |
d0b599781d | ||
![]() |
2431f2058b | ||
![]() |
8c717537c4 | ||
![]() |
e088f4654a | ||
![]() |
b363b6151c | ||
![]() |
3baa4f8223 | ||
![]() |
373f4cfefd | ||
![]() |
64a02b705a | ||
![]() |
f72b5d04e8 | ||
![]() |
1688546519 | ||
![]() |
2eff6b7a3a | ||
![]() |
58962f8470 | ||
![]() |
dfd7ff5b39 | ||
![]() |
d56ded8c18 | ||
![]() |
af3ebbb3c2 | ||
![]() |
1db3359b84 | ||
![]() |
7a40ef74c6 | ||
![]() |
b64cd36468 | ||
![]() |
b3f9983f44 | ||
![]() |
070b08a9e6 | ||
![]() |
851e9ece03 | ||
![]() |
51db63dff7 | ||
![]() |
a5b972d87e | ||
![]() |
4bc3408410 | ||
![]() |
5d392d89ce | ||
![]() |
86f97614b0 | ||
![]() |
5b7bab6752 | ||
![]() |
70042db2de | ||
![]() |
bf69d37cbe | ||
![]() |
ee7ec20f29 | ||
![]() |
1e1ba9afa3 | ||
![]() |
32a75c1ff5 | ||
![]() |
61b99471a1 | ||
![]() |
e27fa882fa | ||
![]() |
7c05914e5a | ||
![]() |
daa438b349 | ||
![]() |
35624ab998 | ||
![]() |
2d6ad41ed4 | ||
![]() |
8544bdd881 | ||
![]() |
a778ff01f6 | ||
![]() |
e7c0ce794b | ||
![]() |
4c50119ac2 | ||
![]() |
2181618357 | ||
![]() |
bec7e54f7f | ||
![]() |
dc317220b3 | ||
![]() |
981750a48a | ||
![]() |
1df717084b | ||
![]() |
f2caf14d6a | ||
![]() |
c9672b35ce | ||
![]() |
39512da74e | ||
![]() |
0d44e7ec27 | ||
![]() |
24082d169b | ||
![]() |
3debaf0f41 | ||
![]() |
2448266d7c | ||
![]() |
3a426e258b | ||
![]() |
b062d1ee3e | ||
![]() |
ebc171d405 | ||
![]() |
4951be6999 | ||
![]() |
01a71132d5 | ||
![]() |
a421a90e0a | ||
![]() |
1d558e8391 | ||
![]() |
30a697e708 | ||
![]() |
0233daeda2 | ||
![]() |
48372bcd91 | ||
![]() |
44a1a1ebde | ||
![]() |
2b07be1d09 | ||
![]() |
a98685d89e | ||
![]() |
1c4ba20646 | ||
![]() |
d3954b94d3 | ||
![]() |
7daea18907 | ||
![]() |
0c84235a95 | ||
![]() |
aab29cb0ab | ||
![]() |
566c5057f9 | ||
![]() |
cd52d7bcf6 | ||
![]() |
ed3ec66d33 | ||
![]() |
01c463c8e8 | ||
![]() |
872465df40 | ||
![]() |
b6f74287d0 | ||
![]() |
013684b5ca | ||
![]() |
90c2955a71 | ||
![]() |
7be868db12 | ||
![]() |
703f84e5e1 | ||
![]() |
c92dbb10ac | ||
![]() |
48f5099646 | ||
![]() |
b9aeb19834 | ||
![]() |
193926c795 | ||
![]() |
a4c3491f0c | ||
![]() |
ad65856b3d | ||
![]() |
b2aac9f991 | ||
![]() |
a582786655 | ||
![]() |
953e994c88 | ||
![]() |
9a6e91d3e5 | ||
![]() |
fc4e7a2dee | ||
![]() |
a56014bb66 | ||
![]() |
ebfc438bd4 | ||
![]() |
3996f02dea | ||
![]() |
d637524bfc | ||
![]() |
8570922dcc | ||
![]() |
6598265f9b | ||
![]() |
8f398dfd08 | ||
![]() |
68e9d701de | ||
![]() |
67e8a00b6d | ||
![]() |
11b48bc374 | ||
![]() |
fa80c62b28 | ||
![]() |
7f9cc67518 | ||
![]() |
84eb6fd460 | ||
![]() |
2cc5a29b86 | ||
![]() |
b178c08271 | ||
![]() |
9a3b208ac5 | ||
![]() |
2989155f05 | ||
![]() |
50b56c64f5 | ||
![]() |
be3e331afb | ||
![]() |
9d8fdd0b20 | ||
![]() |
033c7abe62 | ||
![]() |
5525a21696 | ||
![]() |
aed03cd03b | ||
![]() |
5a5661f136 | ||
![]() |
a3bcf92ea5 | ||
![]() |
0ad31c90f6 | ||
![]() |
789d68e80d | ||
![]() |
f06a1e8b49 | ||
![]() |
a0ca243955 | ||
![]() |
9e2268bd74 | ||
![]() |
702d085117 | ||
![]() |
356a0d72c3 | ||
![]() |
7fcc4a5283 | ||
![]() |
15edfcd088 | ||
![]() |
278d204d1c | ||
![]() |
39b3e7e507 | ||
![]() |
8487dd7cfd | ||
![]() |
5d05d7936c | ||
![]() |
0afd2fe720 | ||
![]() |
59a79a30da | ||
![]() |
5da380e1b0 | ||
![]() |
2adf745d06 | ||
![]() |
ef2eb0764c | ||
![]() |
9e37d7051c | ||
![]() |
05098c3382 | ||
![]() |
32607ee74c | ||
![]() |
30559cd2d3 | ||
![]() |
8fd905215f | ||
![]() |
270d75afe2 | ||
![]() |
a65f97ac75 | ||
![]() |
53c1856038 | ||
![]() |
56b8af86d7 | ||
![]() |
e852df3179 | ||
![]() |
7c03f716a8 | ||
![]() |
d4ba603cf7 | ||
![]() |
deb0306347 | ||
![]() |
881da25e8c | ||
![]() |
a37b953e72 | ||
![]() |
bba5188594 | ||
![]() |
7dc633581d | ||
![]() |
bd00d012e2 | ||
![]() |
4f4476b79f | ||
![]() |
0b8721c25e | ||
![]() |
869e275e48 | ||
![]() |
e4714870a4 | ||
![]() |
1dbd7066de | ||
![]() |
022e4986ee | ||
![]() |
2c9c413e79 | ||
![]() |
b380d5e2c7 | ||
![]() |
650853c177 | ||
![]() |
ae698f988a | ||
![]() |
f920fdecfe | ||
![]() |
4e4f823141 | ||
![]() |
b421f7ae87 | ||
![]() |
ca80d0489b | ||
![]() |
1ed82426a1 | ||
![]() |
7e9f7d4101 | ||
![]() |
f240a33935 | ||
![]() |
5bd0a26126 | ||
![]() |
cc011e39ce | ||
![]() |
29ee551b06 | ||
![]() |
5b5e4157e3 | ||
![]() |
3c19692312 | ||
![]() |
24accf96a8 | ||
![]() |
f2626b0fc0 | ||
![]() |
cc9e9b109c | ||
![]() |
adead9b578 | ||
![]() |
8527d231e1 | ||
![]() |
4c19341279 | ||
![]() |
075fd4da2d | ||
![]() |
52bee88ad2 | ||
![]() |
52b784e0e5 | ||
![]() |
3521732597 | ||
![]() |
d3eec72e45 | ||
![]() |
79bac912aa | ||
![]() |
2381e323c1 | ||
![]() |
d64cedd3fc | ||
![]() |
c16d83fab0 | ||
![]() |
cbed841414 | ||
![]() |
67022beca0 | ||
![]() |
6e76a72d78 | ||
![]() |
a5575bc3a0 | ||
![]() |
90b18158fc | ||
![]() |
71b0e8e937 | ||
![]() |
4d5e0ca7a3 | ||
![]() |
b8685f2c39 | ||
![]() |
c4e697d797 | ||
![]() |
3502a39181 | ||
![]() |
4a56c92e7b | ||
![]() |
2aab617c2e | ||
![]() |
471bd83eb2 | ||
![]() |
ec7a7f4c25 | ||
![]() |
bb0c0af189 | ||
![]() |
709aeff9ea | ||
![]() |
34f18122f5 | ||
![]() |
7208282431 | ||
![]() |
204e72e9eb | ||
![]() |
f626dfb7b7 | ||
![]() |
08b263bf4e | ||
![]() |
d0e99923fd | ||
![]() |
1750512477 | ||
![]() |
5e3b4c3a11 | ||
![]() |
0c431d9746 | ||
![]() |
438c5d9909 | ||
![]() |
7d29edf6f4 | ||
![]() |
50d505b896 | ||
![]() |
2e06f5b1e8 | ||
![]() |
3adcbb7904 | ||
![]() |
27e98147ef | ||
![]() |
48958cc638 | ||
![]() |
ae4de2782a | ||
![]() |
dcf89e0dbd | ||
![]() |
35d92f43c0 | ||
![]() |
12db60885f | ||
![]() |
d637260dc3 | ||
![]() |
0937064e18 | ||
![]() |
54264efb20 | ||
![]() |
6a1d3de75b | ||
![]() |
3fc8c7d560 | ||
![]() |
56e1d577fd | ||
![]() |
272129f66c | ||
![]() |
0441ede229 | ||
![]() |
f001846e00 | ||
![]() |
0ac627dfbb | ||
![]() |
1a82a3bf7b | ||
![]() |
a2d84886c0 | ||
![]() |
97e8382a41 | ||
![]() |
88738327fd | ||
![]() |
0ad65f4748 | ||
![]() |
9dad436f72 | ||
![]() |
ebdc38fff2 | ||
![]() |
97bbe6f305 | ||
![]() |
2f0dbef1e5 | ||
![]() |
1711286ef0 | ||
![]() |
099d3f7b41 | ||
![]() |
5f77478841 | ||
![]() |
07cc3aa5c0 | ||
![]() |
ff26f54bfd | ||
![]() |
3323d2ed37 | ||
![]() |
7b9b3f1ee2 | ||
![]() |
cae98451e3 | ||
![]() |
83da7569f5 | ||
![]() |
26397ee8ad | ||
![]() |
dcfebcb973 | ||
![]() |
fd3b47908b | ||
![]() |
217d2aeb7f | ||
![]() |
e57c701837 | ||
![]() |
b4aa8376de | ||
![]() |
d4976ac47a | ||
![]() |
b8a96d9a77 | ||
![]() |
f75d582eee | ||
![]() |
18e0ec9a55 | ||
![]() |
68b80cdadc | ||
![]() |
3d3c475d1b | ||
![]() |
6668e72351 | ||
![]() |
b72695f01a | ||
![]() |
65f98c1f30 | ||
![]() |
c6fd4c51cf | ||
![]() |
3c11c8441f | ||
![]() |
3261c5b071 | ||
![]() |
c8625c70dd | ||
![]() |
396e9d0c39 | ||
![]() |
e16d3bf040 | ||
![]() |
2c58a87982 | ||
![]() |
a705ae5278 | ||
![]() |
a426e93011 | ||
![]() |
1c220d25ca | ||
![]() |
039858dca2 | ||
![]() |
4758b5efe8 | ||
![]() |
36e065ab4d | ||
![]() |
076497e14d | ||
![]() |
e8d9891d13 | ||
![]() |
4902d7fb9e | ||
![]() |
a873ca6a3e | ||
![]() |
a86cb27cfe | ||
![]() |
d620930f10 | ||
![]() |
8db8ecfef3 | ||
![]() |
a0f99393f5 | ||
![]() |
ae8c3b02d0 | ||
![]() |
99427c2ef7 | ||
![]() |
334f16c0b6 | ||
![]() |
7a80a772c1 | ||
![]() |
8b1d712e1e | ||
![]() |
a121f5b61b | ||
![]() |
251062170e | ||
![]() |
92e59af4d8 | ||
![]() |
fa4c23b76e | ||
![]() |
a0a46850f5 | ||
![]() |
4cd0563a93 | ||
![]() |
b83f0f461c | ||
![]() |
3668ede0ff | ||
![]() |
945584384a | ||
![]() |
b1dd27b516 | ||
![]() |
dcba1aad10 | ||
![]() |
acc8b9cdbc | ||
![]() |
6cbdd825eb | ||
![]() |
cc55f6015d | ||
![]() |
95718ab6ec | ||
![]() |
feab27295d | ||
![]() |
ca176c7549 | ||
![]() |
f8a7a0d6e8 | ||
![]() |
0a05611e1d | ||
![]() |
1a8aaa3b26 | ||
![]() |
cd1c13b4b1 | ||
![]() |
d87c5035dd | ||
![]() |
77967731d5 | ||
![]() |
0b54034470 | ||
![]() |
8ad2583785 | ||
![]() |
dd533a9ab4 | ||
![]() |
7236589037 | ||
![]() |
2c583c3071 | ||
![]() |
5b989adebc | ||
![]() |
855e606163 | ||
![]() |
20f8d3c8a9 | ||
![]() |
0c77dbb7ea | ||
![]() |
2448e2ae3b | ||
![]() |
d53d7aa2e2 | ||
![]() |
0be1df7ee8 | ||
![]() |
6aef0e145c | ||
![]() |
4f1965fbaa | ||
![]() |
32dc54ce72 | ||
![]() |
548736f432 | ||
![]() |
6790f8af08 | ||
![]() |
89d587e7dd | ||
![]() |
5c8b2ebf7a | ||
![]() |
5ac8ba9bae | ||
![]() |
8f8afd98a5 | ||
![]() |
49fb16e2c6 | ||
![]() |
0db47a8586 | ||
![]() |
cec8978886 | ||
![]() |
0160e3fa87 | ||
![]() |
545bfa6ef9 | ||
![]() |
c596b5a17d | ||
![]() |
84dd26c1b7 | ||
![]() |
a765903a41 | ||
![]() |
25ab381916 | ||
![]() |
4d3df5d98f | ||
![]() |
6fe2024542 | ||
![]() |
7f69a0bc5e | ||
![]() |
5b829adedb | ||
![]() |
4cd4c13b2d | ||
![]() |
86be5df475 | ||
![]() |
8c5e0cd4e9 | ||
![]() |
a9b0b8adc8 | ||
![]() |
ba01cb82f7 | ||
![]() |
223b97b884 | ||
![]() |
d437027f26 | ||
![]() |
9a564ee204 | ||
![]() |
d37ee89e84 | ||
![]() |
5de86d3d91 | ||
![]() |
4b6041302e | ||
![]() |
536444f9d1 | ||
![]() |
ba5ec57e4d | ||
![]() |
9fa53ccf05 | ||
![]() |
5c2bac4b9d | ||
![]() |
ef1e24cec2 | ||
![]() |
001e2ad287 | ||
![]() |
0404ec9881 | ||
![]() |
6ff5c88ebf | ||
![]() |
1e761c31bd | ||
![]() |
ab7b2d729e | ||
![]() |
271a07a7d6 | ||
![]() |
029293a086 | ||
![]() |
cf1630a94a | ||
![]() |
4634ace74e | ||
![]() |
3733715184 | ||
![]() |
bf37ac53a3 | ||
![]() |
50934e6840 | ||
![]() |
891ea48e11 | ||
![]() |
70323b9477 | ||
![]() |
451cd4c74a | ||
![]() |
7d0951a08a | ||
![]() |
d98ca9a202 | ||
![]() |
3c2c5bedc5 | ||
![]() |
749d34cd30 | ||
![]() |
1898be2fe1 | ||
![]() |
af34da4160 | ||
![]() |
fa053b7e60 | ||
![]() |
4588219e31 | ||
![]() |
6e89346f00 | ||
![]() |
2703c9899a | ||
![]() |
d05c358fd2 | ||
![]() |
7c6bc5c421 | ||
![]() |
798c398f23 | ||
![]() |
4cc2cc4ad4 | ||
![]() |
dcfdca6351 | ||
![]() |
832599b8c5 | ||
![]() |
c81ef2669e | ||
![]() |
19d837c222 | ||
![]() |
4f48ddfaec | ||
![]() |
2e01fe0b5b | ||
![]() |
7d067d8c30 | ||
![]() |
b3b326738c | ||
![]() |
2bb2caf2d2 | ||
![]() |
2a094883ad | ||
![]() |
6d1126b8aa | ||
![]() |
26a47537f9 | ||
![]() |
01d43c69fb | ||
![]() |
7db1989093 | ||
![]() |
32eee3365a | ||
![]() |
95b4bde918 | ||
![]() |
0a97e28aab | ||
![]() |
4fa8a3898a | ||
![]() |
caa997fff1 | ||
![]() |
bd68b977d5 | ||
![]() |
98a6907976 | ||
![]() |
e9d2182390 | ||
![]() |
5e29f2c1b7 | ||
![]() |
44e28f96e0 | ||
![]() |
a0e81da8c5 | ||
![]() |
85a7c3c60d | ||
![]() |
01e07ca0bc | ||
![]() |
1468ee5fde | ||
![]() |
49f044ecde | ||
![]() |
37f32ab197 | ||
![]() |
a60231ba59 | ||
![]() |
c8dafede6d | ||
![]() |
210191b251 | ||
![]() |
7deea9eb75 | ||
![]() |
5e9a46d54d | ||
![]() |
be64930ebb | ||
![]() |
488974dd3e | ||
![]() |
5975b9125f | ||
![]() |
f301296f1e | ||
![]() |
407477dc68 | ||
![]() |
eb5ad232a0 | ||
![]() |
1b0ce9a123 | ||
![]() |
7b855c851d | ||
![]() |
ca062140f3 | ||
![]() |
a15c391e6c | ||
![]() |
ba636b17a0 | ||
![]() |
2a07eb84f6 | ||
![]() |
949b9fb10e | ||
![]() |
ae1b6af0d4 | ||
![]() |
dd27504d30 | ||
![]() |
c30a08cfc5 | ||
![]() |
a4c49aa35e | ||
![]() |
ba3a61f623 | ||
![]() |
402fba734a | ||
![]() |
262af0678f | ||
![]() |
3765d558b6 | ||
![]() |
4ed8ded502 | ||
![]() |
ee380c5377 | ||
![]() |
7fa25ca7ae | ||
![]() |
d3c401ed4e | ||
![]() |
cb8dae1ddb | ||
![]() |
59deebc961 | ||
![]() |
0ae73296cf | ||
![]() |
8e78fb4caa | ||
![]() |
336da25463 | ||
![]() |
ffbc8c5f70 | ||
![]() |
10d8cfde85 | ||
![]() |
3c7f83407b | ||
![]() |
fe4b668107 | ||
![]() |
cc87b32206 | ||
![]() |
9114867578 | ||
![]() |
6f221852a2 | ||
![]() |
c842994df5 | ||
![]() |
202901b09f | ||
![]() |
7c0fe1285a | ||
![]() |
6dc2c680c5 | ||
![]() |
9a4b56db6e | ||
![]() |
8180f2c742 | ||
![]() |
a76a895f1d | ||
![]() |
b404458369 | ||
![]() |
c44b9f8659 | ||
![]() |
bef1ac2668 | ||
![]() |
906f8fc2e7 | ||
![]() |
c600c6da63 | ||
![]() |
e789a18553 | ||
![]() |
3f3c986932 | ||
![]() |
3ccc5babc1 | ||
![]() |
fafb2eba69 | ||
![]() |
33cf82a982 | ||
![]() |
bd7cd32f91 | ||
![]() |
0a5400263b | ||
![]() |
17d59d3337 | ||
![]() |
9cb3531e2d | ||
![]() |
b50e6e0d90 | ||
![]() |
a88436c620 | ||
![]() |
45cf90094a | ||
![]() |
ae1df20893 | ||
![]() |
71148740d4 | ||
![]() |
4ef583c844 | ||
![]() |
16f61ea96d | ||
![]() |
32f9616b6e | ||
![]() |
1346eb4f76 | ||
![]() |
d4268ba070 | ||
![]() |
696290527a | ||
![]() |
686866c6f9 | ||
![]() |
72455ccde1 | ||
![]() |
88fb860568 | ||
![]() |
ceed494cf7 | ||
![]() |
ac8f919304 | ||
![]() |
7f1ec4802d | ||
![]() |
d5777b7bce | ||
![]() |
e34f179a82 | ||
![]() |
6a90b9a512 | ||
![]() |
ce5ee65d60 | ||
![]() |
65a8dbfe41 | ||
![]() |
1ff667b7ef | ||
![]() |
0806c7fbdb | ||
![]() |
5708d7a6b8 | ||
![]() |
2264cc5d10 | ||
![]() |
28cb3f9d0c | ||
![]() |
f16ed4697f | ||
![]() |
81e412fe5b | ||
![]() |
f3137eb0a9 | ||
![]() |
0e5bd4ed74 | ||
![]() |
6a37f25c5d | ||
![]() |
2afff05014 | ||
![]() |
e3d9417b84 | ||
![]() |
6a188033c6 | ||
![]() |
df24fd7bf2 | ||
![]() |
d45b4ad134 | ||
![]() |
ec938f254c | ||
![]() |
fa82e759bd | ||
![]() |
daf0bcfac3 | ||
![]() |
09428dcade | ||
![]() |
49d51c5af8 | ||
![]() |
ec7f69d559 | ||
![]() |
a8c5bf2573 | ||
![]() |
f12de61d7f | ||
![]() |
71fcd6482d | ||
![]() |
e59b38a5d2 | ||
![]() |
b07658b460 | ||
![]() |
5b8d9d1510 | ||
![]() |
6169c458bf | ||
![]() |
23400d0449 | ||
![]() |
992d11be10 | ||
![]() |
feac45fd0a | ||
![]() |
934176818f | ||
![]() |
c5435ec1fa | ||
![]() |
c3f472fbcb | ||
![]() |
1cee1c3562 | ||
![]() |
eae5a74a11 | ||
![]() |
5df1a223c2 | ||
![]() |
81382a4bc0 | ||
![]() |
750a429bd4 | ||
![]() |
dbe78dff93 | ||
![]() |
5869627b32 | ||
![]() |
5bc5ef0ae8 | ||
![]() |
2cfd2e1410 | ||
![]() |
cce5e8f811 | ||
![]() |
058e66c7fc | ||
![]() |
8ae09c3d02 | ||
![]() |
83699bc5c3 | ||
![]() |
3a5eb8c5d2 | ||
![]() |
fd00e5eaae | ||
![]() |
20444ee7d5 | ||
![]() |
eb06e575c2 | ||
![]() |
b4315152e2 | ||
![]() |
340a9f8b76 | ||
![]() |
81cec6a25a | ||
![]() |
c5b31e2f63 | ||
![]() |
0642b17ab0 | ||
![]() |
ac689a9a2e | ||
![]() |
43cc4ec009 | ||
![]() |
9cc333b2bd | ||
![]() |
55d98ff935 | ||
![]() |
dc90a4ed42 | ||
![]() |
ef80abd885 | ||
![]() |
b9080f9479 | ||
![]() |
092a5a1ca3 | ||
![]() |
d3c31c52a2 | ||
![]() |
329173e145 | ||
![]() |
8e4f8fcbc8 | ||
![]() |
c1c707c77a | ||
![]() |
b1e58dd10d | ||
![]() |
2cc0aceac7 | ||
![]() |
d79d5dbadd | ||
![]() |
ebd8a10cef | ||
![]() |
be7475fe66 | ||
![]() |
85e2f7eb34 | ||
![]() |
9bb979f2e8 | ||
![]() |
c942345453 | ||
![]() |
fcc344ba99 | ||
![]() |
a3b28871c6 | ||
![]() |
2ca034bfa8 | ||
![]() |
0d035a08d6 | ||
![]() |
6655e2664e | ||
![]() |
c51ce2eec7 | ||
![]() |
b2e00eb868 | ||
![]() |
820df0c596 | ||
![]() |
865e3dcd0c | ||
![]() |
f55e2fc7b8 | ||
![]() |
cb22e1933e | ||
![]() |
8818a9e82a | ||
![]() |
dbb47d506c | ||
![]() |
e4d5d77347 | ||
![]() |
dd52124765 | ||
![]() |
6f3267aee9 | ||
![]() |
0bbd6b7c4b | ||
![]() |
1cc47b02cf | ||
![]() |
e7babc071d | ||
![]() |
d11ca2e159 | ||
![]() |
2d6365dc6a | ||
![]() |
cc24119a59 | ||
![]() |
b8f15333d8 | ||
![]() |
e5a426419c | ||
![]() |
8a1df1e700 | ||
![]() |
cc298f56e2 | ||
![]() |
665693a513 | ||
![]() |
c35e82e59f | ||
![]() |
72d38f4cff | ||
![]() |
f6c47ac19c | ||
![]() |
0430dc5a1f | ||
![]() |
27bec89386 | ||
![]() |
79b3ce141c | ||
![]() |
011afd0910 | ||
![]() |
8d00e4d31d | ||
![]() |
8688777ae1 | ||
![]() |
198aceee07 | ||
![]() |
ee1bf1c221 | ||
![]() |
24ecf1a166 | ||
![]() |
5cb49c86a0 | ||
![]() |
9940d2590b | ||
![]() |
577fce69e2 | ||
![]() |
e68375a71e | ||
![]() |
fe7fb6c7a9 | ||
![]() |
cec67d8eff | ||
![]() |
2443e2ec7c | ||
![]() |
bcf862044a | ||
![]() |
b73f049484 | ||
![]() |
cde5314d41 | ||
![]() |
ed5eb28228 | ||
![]() |
db5e66a9b0 | ||
![]() |
77723b615f | ||
![]() |
96c7716d3e | ||
![]() |
ea80c1ed83 | ||
![]() |
7d7b7c1e25 | ||
![]() |
9d860c265c | ||
![]() |
d234b4b0f1 | ||
![]() |
e1598a9966 | ||
![]() |
e82f38e277 | ||
![]() |
1d27aa2f18 | ||
![]() |
113dbbdf94 | ||
![]() |
e701b5b5eb | ||
![]() |
6f01896d04 | ||
![]() |
5582bbac60 | ||
![]() |
d2401a212a | ||
![]() |
52548542d2 | ||
![]() |
509cbdf476 | ||
![]() |
5a6ebd6657 | ||
![]() |
541d3f286a | ||
![]() |
0191af412b | ||
![]() |
abcd2a2d01 | ||
![]() |
91efcb910b | ||
![]() |
e9b7320d1c | ||
![]() |
779744bd8e | ||
![]() |
1123d85fd2 | ||
![]() |
5c247d2833 | ||
![]() |
17fdb4f5c9 | ||
![]() |
365c1987ed | ||
![]() |
c03422ee37 | ||
![]() |
ef8f23ce90 | ||
![]() |
050f7ebddf | ||
![]() |
10fdf46410 | ||
![]() |
49976ae35a | ||
![]() |
21afb05409 | ||
![]() |
0af1ec3bde | ||
![]() |
343d766ddd | ||
![]() |
02cae2e61e | ||
![]() |
1975c9c34a | ||
![]() |
42decbddd9 | ||
![]() |
d88bb3c668 | ||
![]() |
b71467b9be | ||
![]() |
ac503ef72e | ||
![]() |
ea608cecb0 | ||
![]() |
2e266ec945 | ||
![]() |
bec1adf7ad | ||
![]() |
2fc2f47d06 | ||
![]() |
3a8e19d1b4 | ||
![]() |
cc9db0220c | ||
![]() |
13ed78be96 | ||
![]() |
cd1ede38a7 | ||
![]() |
1ec68c428a | ||
![]() |
f6ed3bc9db | ||
![]() |
34cdda2e28 | ||
![]() |
4ab2c907f5 | ||
![]() |
8632d99341 | ||
![]() |
efa32ed4f6 | ||
![]() |
551b8af76d | ||
![]() |
887254f5da | ||
![]() |
0275bd1d45 | ||
![]() |
f3891c305d | ||
![]() |
5874b78349 | ||
![]() |
a812fc07f2 | ||
![]() |
78e7312adf | ||
![]() |
593970ed6d | ||
![]() |
693bf4816b | ||
![]() |
b8b11880b0 | ||
![]() |
0ff4016250 | ||
![]() |
e753f3849e | ||
![]() |
7b248c8fb2 | ||
![]() |
f0e4dd90ee | ||
![]() |
c540ec5417 | ||
![]() |
3615ab535b | ||
![]() |
b8d6ebe882 | ||
![]() |
1f27872294 | ||
![]() |
3815516022 | ||
![]() |
a1dfdced31 | ||
![]() |
ff39a8db3f | ||
![]() |
9877e899e0 | ||
![]() |
bc4a6138b1 | ||
![]() |
f825cab54a | ||
![]() |
57b01c2711 | ||
![]() |
5f3e3d7796 | ||
![]() |
bee75a4508 | ||
![]() |
a4ac444bda | ||
![]() |
d13b8c8421 | ||
![]() |
0af3063127 | ||
![]() |
17e1975dd8 | ||
![]() |
166eeacab9 | ||
![]() |
296df2660a | ||
![]() |
017169f1a1 | ||
![]() |
ed0e73803f | ||
![]() |
f4475b9d2a | ||
![]() |
b9849da66e | ||
![]() |
8a1df9afee | ||
![]() |
9cfb56d9c6 | ||
![]() |
25ade86994 | ||
![]() |
da484b07f5 | ||
![]() |
20c0916adc | ||
![]() |
c10ec2962b | ||
![]() |
de5632b1cf | ||
![]() |
1bb06106fd | ||
![]() |
bc18cdbeea | ||
![]() |
67df5d6dcc | ||
![]() |
68a7282b27 | ||
![]() |
5732b66c5a | ||
![]() |
f1cce8ef46 | ||
![]() |
e47932c9aa | ||
![]() |
3a70fb0e15 | ||
![]() |
d5da84dcb2 | ||
![]() |
1bd051d703 | ||
![]() |
abb55f8e4b | ||
![]() |
46f6a257d0 | ||
![]() |
97e4eec5be | ||
![]() |
69b99abb7d | ||
![]() |
08f6d5a683 | ||
![]() |
627bea30ed | ||
![]() |
caf0a5bbe8 | ||
![]() |
501f988b0f | ||
![]() |
5f0c6fce74 | ||
![]() |
78089107b7 | ||
![]() |
f3b0906cea | ||
![]() |
9f581c6181 | ||
![]() |
31b232d3f3 | ||
![]() |
77e6a6de61 | ||
![]() |
85702a8142 | ||
![]() |
0bcdc9641d | ||
![]() |
bfd1bdfc64 | ||
![]() |
c1fed6d469 | ||
![]() |
79ffe021e4 | ||
![]() |
a1c348b365 | ||
![]() |
904b1f114b | ||
![]() |
63f481d498 | ||
![]() |
fc5c19788e | ||
![]() |
56e0bde8ab | ||
![]() |
db2cdca71b | ||
![]() |
faa2fba6b9 | ||
![]() |
f727ea2874 | ||
![]() |
4a7ef07089 | ||
![]() |
0df726cdab | ||
![]() |
2f6f0d2f3e | ||
![]() |
fae5938b0a | ||
![]() |
2ec3639900 | ||
![]() |
4513404629 | ||
![]() |
13bfda56ef | ||
![]() |
075c164407 | ||
![]() |
dcb5049f97 | ||
![]() |
d24cf14009 | ||
![]() |
3d34802ab2 | ||
![]() |
2aec21a6d0 | ||
![]() |
19ccff9ff1 | ||
![]() |
1dd9c6754c | ||
![]() |
f9467ceaf1 | ||
![]() |
ccce0f2d3b | ||
![]() |
f51da4f0c4 | ||
![]() |
2eff389fff | ||
![]() |
77ca2ec0e9 | ||
![]() |
77e6ea8a6f | ||
![]() |
e33299bbd7 | ||
![]() |
74e273274c | ||
![]() |
02bf5902f0 | ||
![]() |
5f41de8938 | ||
![]() |
f71d839009 | ||
![]() |
3cb6b17316 | ||
![]() |
c0cb677a4c | ||
![]() |
a5a6a8eaee | ||
![]() |
ecb861de56 | ||
![]() |
1d20f18d3f | ||
![]() |
68f6b3452e | ||
![]() |
60075f6e8c | ||
![]() |
1d7789f544 | ||
![]() |
bd10a9aa26 | ||
![]() |
1ceeed38bc | ||
![]() |
2e1e917952 | ||
![]() |
9e0b91ac17 | ||
![]() |
ea83ec496b | ||
![]() |
f26bb26ffa | ||
![]() |
28348f919b | ||
![]() |
c84d4c637d | ||
![]() |
04706cfa9c | ||
![]() |
3078bea7cc | ||
![]() |
c91e0fc988 | ||
![]() |
5183af4e8a | ||
![]() |
1ff5cb0596 | ||
![]() |
f441ce9c17 | ||
![]() |
522e5e7957 | ||
![]() |
9e468bd059 | ||
![]() |
ecc9443677 | ||
![]() |
6b8d8bf735 | ||
![]() |
43d024fe42 | ||
![]() |
c016dedea4 | ||
![]() |
b9ca2cfe90 | ||
![]() |
d3492b5b6c | ||
![]() |
3682cd6a5e | ||
![]() |
b209b1e481 | ||
![]() |
4afed48f58 | ||
![]() |
6a18ce078e | ||
![]() |
bc5aadff7d | ||
![]() |
de89b516dc | ||
![]() |
4dbe9a7015 | ||
![]() |
a320f16404 | ||
![]() |
cec7241986 | ||
![]() |
6de77293ad | ||
![]() |
0ac53d8353 | ||
![]() |
da79b93387 | ||
![]() |
b88c300d04 | ||
![]() |
88372cd516 | ||
![]() |
b3206121cc | ||
![]() |
5bccb5fc42 | ||
![]() |
47e1cbdaff | ||
![]() |
52020cbe26 | ||
![]() |
d33651c25b | ||
![]() |
18e9f9c304 | ||
![]() |
2607c44fbb | ||
![]() |
c8a7d44f55 | ||
![]() |
a6e536189c | ||
![]() |
3f9a6cae53 | ||
![]() |
cd5494fdd2 | ||
![]() |
5ab1b14d6b | ||
![]() |
379f181362 | ||
![]() |
dd19aa63d0 | ||
![]() |
6df3b417a9 | ||
![]() |
fa76431dd6 | ||
![]() |
6504fb68b6 | ||
![]() |
2ff49cf657 | ||
![]() |
fa1aa1fe80 | ||
![]() |
85b95a2940 | ||
![]() |
5e38039c4d | ||
![]() |
4df936a437 | ||
![]() |
736a8b1b80 | ||
![]() |
22f6128bc4 | ||
![]() |
db475b6998 | ||
![]() |
6daf7f6322 | ||
![]() |
16086c0961 | ||
![]() |
e88c81ad0d | ||
![]() |
a2b9aed40d | ||
![]() |
caa4fe1ec4 | ||
![]() |
25fb878e54 | ||
![]() |
a1d6ffadad | ||
![]() |
3b89814b69 | ||
![]() |
071e0be445 | ||
![]() |
bed364d75e | ||
![]() |
d2984e9e16 | ||
![]() |
05a8c692f2 | ||
![]() |
fd9570e782 | ||
![]() |
35d26afcb4 | ||
![]() |
2245ee6fce | ||
![]() |
ff10130176 | ||
![]() |
a13f1a9bee | ||
![]() |
3cc60fa4d4 | ||
![]() |
bd69c24231 | ||
![]() |
c7d292a716 | ||
![]() |
74bc159a52 | ||
![]() |
9f99a1896d | ||
![]() |
8110259d1c | ||
![]() |
1048bf993a | ||
![]() |
3eba0f4e0d | ||
![]() |
d7e0b364d1 | ||
![]() |
25dd43b949 | ||
![]() |
8e8ffa30a6 | ||
![]() |
69da2f4b23 | ||
![]() |
d48bab02a1 | ||
![]() |
886120fe9f | ||
![]() |
bdd4d9f3ff | ||
![]() |
6276c2f1f5 | ||
![]() |
b3acc97d03 | ||
![]() |
24bd1db4fc | ||
![]() |
e83d3cb4a3 | ||
![]() |
674481f0d1 | ||
![]() |
5d271d21bc | ||
![]() |
4e4493e627 | ||
![]() |
1154905818 | ||
![]() |
0d3ea848c2 | ||
![]() |
8ffe1e65fd | ||
![]() |
127ea7e351 | ||
![]() |
34b16f1919 | ||
![]() |
fa32faf759 | ||
![]() |
6f3d7e76c9 | ||
![]() |
57665e8964 | ||
![]() |
5c502b1fe4 | ||
![]() |
6d1410741d | ||
![]() |
3e20724058 | ||
![]() |
197f47befe | ||
![]() |
d126611e87 | ||
![]() |
1b75be5710 | ||
![]() |
6272969983 | ||
![]() |
084fc2fcd1 | ||
![]() |
b2e68db380 | ||
![]() |
459156fe57 | ||
![]() |
7abfe68458 | ||
![]() |
442d7a7226 | ||
![]() |
80ebcb2c28 | ||
![]() |
9735d1c6f3 | ||
![]() |
ef017fd343 | ||
![]() |
cd928bc586 | ||
![]() |
d2b4d25317 | ||
![]() |
a418cd2a2a | ||
![]() |
94cdd88474 | ||
![]() |
1bdf3876fc | ||
![]() |
30435e6406 | ||
![]() |
bd1c06a7a7 | ||
![]() |
8358272b9a | ||
![]() |
ff3cce0ed2 | ||
![]() |
c3787af29d | ||
![]() |
b3810a16cc | ||
![]() |
38539aac74 | ||
![]() |
58e8346209 | ||
![]() |
ba9ce4adf2 | ||
![]() |
a3a8fa1cef | ||
![]() |
c6805271e9 | ||
![]() |
bd60fe5a13 | ||
![]() |
b72f3baab7 | ||
![]() |
d1d54ce9c8 | ||
![]() |
954f26308b | ||
![]() |
1cafa0b33f | ||
![]() |
18c17168e1 | ||
![]() |
d3a97f1062 | ||
![]() |
d18f078b1e | ||
![]() |
9ffcde878a | ||
![]() |
aa970d6ca5 | ||
![]() |
503c68320a | ||
![]() |
a106342439 | ||
![]() |
00f5471270 | ||
![]() |
3b0e6ec65c | ||
![]() |
8691ddc081 | ||
![]() |
f5199d2b73 | ||
![]() |
0c8d9d5614 | ||
![]() |
24c5935661 | ||
![]() |
bff6697690 | ||
![]() |
02405b4856 | ||
![]() |
78b37b592e | ||
![]() |
4db88cf86b | ||
![]() |
a7e17eabff | ||
![]() |
93d9ce18b2 | ||
![]() |
6f7ac93d84 | ||
![]() |
fa5d60ca26 | ||
![]() |
20ed81cd86 | ||
![]() |
6f38874096 | ||
![]() |
b7db5be4df | ||
![]() |
6d278994ec | ||
![]() |
157dbffc59 | ||
![]() |
f21ab3588d | ||
![]() |
2900bda8f9 | ||
![]() |
5f37c19d42 | ||
![]() |
6f6356e617 | ||
![]() |
5d152baac0 | ||
![]() |
d7739f9764 | ||
![]() |
d6e73fde50 | ||
![]() |
fd149b3f46 | ||
![]() |
d1d45e7166 | ||
![]() |
4b19759dd6 | ||
![]() |
47f44680a3 | ||
![]() |
0a344ada97 | ||
![]() |
3b98d01f71 | ||
![]() |
23d8000239 | ||
![]() |
c32ee40ca0 | ||
![]() |
213f45494f | ||
![]() |
85a51e6c42 | ||
![]() |
ecfdc6f0a8 | ||
![]() |
1c1c3fd832 | ||
![]() |
7bafe995e5 | ||
![]() |
3c23672347 | ||
![]() |
33fe68d7eb | ||
![]() |
b031fa1531 | ||
![]() |
dab4dc3f41 | ||
![]() |
78096803ea | ||
![]() |
f1987b9544 | ||
![]() |
8b6bf08a23 | ||
![]() |
2640203c88 | ||
![]() |
2dafa9644f | ||
![]() |
92ebeddcb0 | ||
![]() |
ecee073e08 | ||
![]() |
6d1b9ffad2 | ||
![]() |
7ee4b54154 | ||
![]() |
e3271b8082 | ||
![]() |
c6ff45f959 | ||
![]() |
5d3f22e06a | ||
![]() |
c77f6c5f7b | ||
![]() |
6d03c3a54c | ||
![]() |
92dc63e5c9 | ||
![]() |
505d319e01 | ||
![]() |
62f845a94e | ||
![]() |
7a9eff7f35 | ||
![]() |
b849ea8eaa | ||
![]() |
f1635fa302 | ||
![]() |
6110b72b87 | ||
![]() |
247de600af | ||
![]() |
3e60d3d96e | ||
![]() |
6de617ecd5 | ||
![]() |
7c8df97968 | ||
![]() |
acf6736afd | ||
![]() |
5f2480c3d9 | ||
![]() |
0593a078c6 | ||
![]() |
323c70dcdf | ||
![]() |
a1b0f84444 | ||
![]() |
df6c271830 | ||
![]() |
12bf04826a | ||
![]() |
52a1b0453c | ||
![]() |
0dcb56eab5 | ||
![]() |
a42f78b08b | ||
![]() |
505768db04 | ||
![]() |
c74db95c14 | ||
![]() |
af3f27feae | ||
![]() |
7b366d49d2 | ||
![]() |
88b30e7e28 | ||
![]() |
05c0febd04 | ||
![]() |
9d70ec56f2 | ||
![]() |
7f8ec59939 | ||
![]() |
0398ec40b7 | ||
![]() |
f55465f8b8 | ||
![]() |
a4384bd340 | ||
![]() |
a2368a75f7 | ||
![]() |
68e5e74882 | ||
![]() |
b2afac8914 | ||
![]() |
0637c1c9d4 | ||
![]() |
0453a5fb3d | ||
![]() |
3ad56ea103 | ||
![]() |
6322ab9f07 | ||
![]() |
21d0f10dd7 | ||
![]() |
51b9e81c3d | ||
![]() |
7723011594 | ||
![]() |
91c11a18e0 | ||
![]() |
215ba35fe1 | ||
![]() |
7e17011ebc | ||
![]() |
1f25edc737 | ||
![]() |
ab13db73e7 | ||
![]() |
cb5eb9edc7 | ||
![]() |
509675fe66 | ||
![]() |
da5c12f466 | ||
![]() |
f2ad10bbce | ||
![]() |
99a7bece2e | ||
![]() |
b305fd8865 | ||
![]() |
c0beb621e2 | ||
![]() |
da6d64e581 | ||
![]() |
a6d58ee360 | ||
![]() |
2a480ab7db | ||
![]() |
5c09ee29db | ||
![]() |
15526bd6e8 | ||
![]() |
c8f48168b4 | ||
![]() |
d937d473f9 | ||
![]() |
66f14c6343 | ||
![]() |
f25fadafd8 | ||
![]() |
d9b0df94e2 | ||
![]() |
200d11ca99 | ||
![]() |
f8c48ef60a | ||
![]() |
1fc0680c71 | ||
![]() |
ec4afb2cbc | ||
![]() |
a2a632c415 | ||
![]() |
962e64106c | ||
![]() |
c40b6088ee | ||
![]() |
0d1a254ca8 | ||
![]() |
fdbcb61a12 | ||
![]() |
6435cb1466 | ||
![]() |
4739cfab9a | ||
![]() |
00a73e9cc8 | ||
![]() |
0ff5dec004 | ||
![]() |
41129cf379 | ||
![]() |
c8c394b4e9 | ||
![]() |
59886a1528 | ||
![]() |
a28d2c869f | ||
![]() |
6f1fff44fa | ||
![]() |
108978d1a5 | ||
![]() |
a4733b4d91 | ||
![]() |
66f4671ec0 | ||
![]() |
c932621b5d | ||
![]() |
21173dc907 | ||
![]() |
f2c30ba3f7 | ||
![]() |
989bdfb0d5 | ||
![]() |
5761dce957 | ||
![]() |
95e2e574b8 | ||
![]() |
c789d80ce5 | ||
![]() |
8422ffcd19 | ||
![]() |
2d30535b69 | ||
![]() |
0a815179a2 | ||
![]() |
aa24e5d284 | ||
![]() |
23aad24027 | ||
![]() |
7ae5459fe7 | ||
![]() |
0a8a88421e | ||
![]() |
4f42a176ef | ||
![]() |
4457067045 | ||
![]() |
e7dc2048ad | ||
![]() |
af6094bb06 | ||
![]() |
0ab139cb24 | ||
![]() |
500a7301f2 | ||
![]() |
c92cef1780 | ||
![]() |
49ceac1ac7 | ||
![]() |
276a93605d | ||
![]() |
0150c3fe23 | ||
![]() |
34674501c3 | ||
![]() |
f6206d4c30 | ||
![]() |
6dcd9596a2 | ||
![]() |
e165838e54 | ||
![]() |
eae1707e97 | ||
![]() |
ed2950f73b | ||
![]() |
7eae8f68d8 | ||
![]() |
0ab2d18b52 | ||
![]() |
8ab621bc91 | ||
![]() |
41e51bbeb5 | ||
![]() |
66d9e8c038 | ||
![]() |
f5e47b2b74 | ||
![]() |
678a823e8b | ||
![]() |
246746a82e | ||
![]() |
00038453e1 | ||
![]() |
9ce485eade | ||
![]() |
8a6ff5a42b | ||
![]() |
1eaac0072d | ||
![]() |
35716a7e4c | ||
![]() |
e0f89beebb | ||
![]() |
b8bf2a707c | ||
![]() |
7d616be8f4 | ||
![]() |
0a7e9f9f8f | ||
![]() |
226e188903 | ||
![]() |
590d454119 | ||
![]() |
06d5bd799f | ||
![]() |
7fe6541b7c | ||
![]() |
0f30fa5fb9 | ||
![]() |
2c362315ad | ||
![]() |
7dd79edc52 | ||
![]() |
3d6f25a9d3 | ||
![]() |
b3c21feba3 | ||
![]() |
fe54fadbf8 | ||
![]() |
caa0722d49 | ||
![]() |
fee32622f1 | ||
![]() |
d1910e4274 | ||
![]() |
45eb11d7f4 | ||
![]() |
874179fa75 | ||
![]() |
8b16cb4bae | ||
![]() |
777ed55c21 | ||
![]() |
fcafd8b05e | ||
![]() |
5f1cadfea4 | ||
![]() |
917a981096 | ||
![]() |
89f8b6060f | ||
![]() |
345346315a | ||
![]() |
f1c27e1798 | ||
![]() |
4316024dc7 | ||
![]() |
ca05aa84ff | ||
![]() |
d10d7dc298 | ||
![]() |
063af1dbc7 | ||
![]() |
ef6fe43251 | ||
![]() |
2659055c31 | ||
![]() |
f5ed757780 | ||
![]() |
e114b842ba | ||
![]() |
12e2116acb | ||
![]() |
bccc97d25f | ||
![]() |
a9bcc75733 | ||
![]() |
7b87475af8 | ||
![]() |
2ac218886a | ||
![]() |
97503897f0 | ||
![]() |
7e06e32cb6 | ||
![]() |
15b813c6af | ||
![]() |
a71c9107bd | ||
![]() |
4c2110189b | ||
![]() |
df750c2a71 | ||
![]() |
3eb412b750 | ||
![]() |
5c7b7e4182 | ||
![]() |
2b0fad87d2 | ||
![]() |
8817d41275 | ||
![]() |
04ef3f9496 | ||
![]() |
fbe26e13ae | ||
![]() |
4408dffa87 | ||
![]() |
546192865c | ||
![]() |
7afa550ba4 | ||
![]() |
21a9372320 | ||
![]() |
f4a2eec984 | ||
![]() |
94b9455c9c | ||
![]() |
c41c71c8c1 | ||
![]() |
dde647c570 | ||
![]() |
445a89ba6e | ||
![]() |
07495f6621 | ||
![]() |
5952edc550 | ||
![]() |
b66a038a2f | ||
![]() |
df1c8a64e5 | ||
![]() |
3e2aebcd10 | ||
![]() |
0d279cb4dd | ||
![]() |
3942a76d15 | ||
![]() |
1bebf3d3d6 | ||
![]() |
d5f54c240a | ||
![]() |
8868ecd11a | ||
![]() |
8a31c57bfa | ||
![]() |
7e2cce4a8d | ||
![]() |
bfb217c203 | ||
![]() |
593e55af95 | ||
![]() |
29af62f956 | ||
![]() |
2b022e1871 | ||
![]() |
783a4d3996 | ||
![]() |
ddfe925f8c | ||
![]() |
1735bcab21 | ||
![]() |
ff6cacf766 | ||
![]() |
2a72eae77f | ||
![]() |
39fb7fdfe3 | ||
![]() |
e0facf35c8 | ||
![]() |
73d48c8ac4 | ||
![]() |
5080059143 | ||
![]() |
90d5b77d74 | ||
![]() |
e0f987328b | ||
![]() |
04c3a925cc | ||
![]() |
219b29991b | ||
![]() |
5120045459 | ||
![]() |
3bca4a73f4 | ||
![]() |
b4857ab036 | ||
![]() |
f3ae041691 | ||
![]() |
a84627a465 | ||
![]() |
78aeca6399 | ||
![]() |
5dd8f0a0b7 | ||
![]() |
9378fc7276 | ||
![]() |
3a0d8069b6 | ||
![]() |
bab42014a4 | ||
![]() |
74fed5695f | ||
![]() |
3be368c333 | ||
![]() |
a4a8e2614e | ||
![]() |
58eb151ba1 | ||
![]() |
21d21ea7f0 | ||
![]() |
45dc5e236d | ||
![]() |
0597eeed6b | ||
![]() |
0a66fe196a | ||
![]() |
3f21b4aa7d | ||
![]() |
b97186c5bf | ||
![]() |
874b24fb32 | ||
![]() |
97889b5e55 | ||
![]() |
4b134194cf | ||
![]() |
24b60a25d5 | ||
![]() |
3e069376a7 | ||
![]() |
5bdb2c9e86 | ||
![]() |
71b0c5f805 | ||
![]() |
a1f62e7a1f | ||
![]() |
186c40801a | ||
![]() |
bf867e84d6 | ||
![]() |
27035a8e1d | ||
![]() |
db3770c3e5 | ||
![]() |
46337ca554 | ||
![]() |
4f0f288000 | ||
![]() |
8060bae1b8 | ||
![]() |
ea32c97b82 | ||
![]() |
99efbd30f1 | ||
![]() |
a9eb7cb4fd | ||
![]() |
a11cab0f88 | ||
![]() |
447324b59f | ||
![]() |
e580e815f0 | ||
![]() |
5fc00840f2 | ||
![]() |
ff59dcd66e | ||
![]() |
0a1d82de2a | ||
![]() |
26837645ac | ||
![]() |
c318699d6a | ||
![]() |
e5caab635a | ||
![]() |
d5f96295ff | ||
![]() |
4a6c78aaf3 | ||
![]() |
c4d214f5c0 | ||
![]() |
1ed7e6cb1c | ||
![]() |
6b7f80f24a | ||
![]() |
220217561a | ||
![]() |
75e857a510 | ||
![]() |
052bdd8eb0 | ||
![]() |
9e54de8a8a | ||
![]() |
fd2809e367 | ||
![]() |
18874c2069 | ||
![]() |
115eb0ddef | ||
![]() |
9940258725 | ||
![]() |
74e14a4fc3 | ||
![]() |
90ea386c62 | ||
![]() |
a5b132dfd7 | ||
![]() |
157debf8e4 | ||
![]() |
74a5528f17 | ||
![]() |
21de9e938a | ||
![]() |
9c4d3cd6e2 | ||
![]() |
0616d184e0 | ||
![]() |
6f8b3a6242 | ||
![]() |
9a006dc84a | ||
![]() |
d2dbaf52a1 | ||
![]() |
0a9145cd3c | ||
![]() |
e778b02c0c | ||
![]() |
8fb5f0ef3c | ||
![]() |
43f5e4360d | ||
![]() |
cdc8640218 | ||
![]() |
21f8d7a967 | ||
![]() |
801d92bbba | ||
![]() |
77116172e4 | ||
![]() |
9d3c0f4ff0 | ||
![]() |
6382d2b730 | ||
![]() |
6ad4493a91 | ||
![]() |
f070dc5527 | ||
![]() |
fc6e7c81d3 | ||
![]() |
59a725c52c | ||
![]() |
51b3d7cb4a | ||
![]() |
3baff4e675 | ||
![]() |
06911e7ad0 | ||
![]() |
4e0f3e35db | ||
![]() |
fd6ce57003 | ||
![]() |
4f55be4f19 | ||
![]() |
bd20c83919 | ||
![]() |
48f8a45031 | ||
![]() |
680cec5abb | ||
![]() |
e8b3c66c21 | ||
![]() |
ecdc3be52e | ||
![]() |
301bdf2186 | ||
![]() |
1c1b67e000 | ||
![]() |
662870baf4 | ||
![]() |
bc85520f5e | ||
![]() |
d3be7a3edf | ||
![]() |
6cd0da821a | ||
![]() |
1333c41811 | ||
![]() |
55eceb4080 | ||
![]() |
e95629f827 | ||
![]() |
6332ee6edb | ||
![]() |
901d56f898 | ||
![]() |
010c3494fd | ||
![]() |
5ebc345e95 | ||
![]() |
b3a768a04b | ||
![]() |
f414af4d82 | ||
![]() |
b743ca8eb1 | ||
![]() |
a3a5bffce4 | ||
![]() |
7971f3cbd8 | ||
![]() |
d1b80aa4d4 | ||
![]() |
08d9f7d967 | ||
![]() |
5f62b4a89d | ||
![]() |
776502e6db | ||
![]() |
39e0a1fb9e | ||
![]() |
5ed02d2f0d | ||
![]() |
72e864b013 | ||
![]() |
5ab012163e | ||
![]() |
1900686bc4 | ||
![]() |
809a294f9d | ||
![]() |
cf3f6ede72 | ||
![]() |
b76a90304d | ||
![]() |
7af5b24b03 | ||
![]() |
1f81fb9284 | ||
![]() |
4e105492ca | ||
![]() |
abf7dd5fa8 | ||
![]() |
6c877b6d44 | ||
![]() |
86d2998dc0 | ||
![]() |
201daf8ff3 | ||
![]() |
1140f5f6cb | ||
![]() |
0a91d60677 | ||
![]() |
5cd8f5681e | ||
![]() |
b2babd9502 | ||
![]() |
10faaaf531 | ||
![]() |
7642c5a50d | ||
![]() |
42dba3170d | ||
![]() |
5c90a742ba | ||
![]() |
9e57ef074d | ||
![]() |
9e9b1c3c98 | ||
![]() |
13d5deddec | ||
![]() |
8522760c70 | ||
![]() |
5bc2282ac5 | ||
![]() |
2944b2a8f6 | ||
![]() |
264480b9bc | ||
![]() |
7d52cc46dc | ||
![]() |
37c9fd278c | ||
![]() |
3d6fe0a565 | ||
![]() |
da488f76d2 | ||
![]() |
6d28de77d1 | ||
![]() |
a8bd46c521 | ||
![]() |
1ec3a74d4c | ||
![]() |
076449a33d | ||
![]() |
1b5cc2abf1 | ||
![]() |
b176225910 | ||
![]() |
082d1eea48 | ||
![]() |
e99dfbae0d | ||
![]() |
d6735bed4f | ||
![]() |
b024d93fa0 | ||
![]() |
dbe8554724 | ||
![]() |
9f79e64678 | ||
![]() |
f513cae66e | ||
![]() |
37b807210e | ||
![]() |
15e26ba929 | ||
![]() |
db1795981f | ||
![]() |
2a40baf509 | ||
![]() |
2dbf72e452 | ||
![]() |
67ae716c60 | ||
![]() |
8dfb130d19 | ||
![]() |
2a42fea32a | ||
![]() |
f69edefddf | ||
![]() |
6f3b5fc559 | ||
![]() |
7275a34e1f | ||
![]() |
687e2ecaaa | ||
![]() |
64312aedae | ||
![]() |
391876e557 | ||
![]() |
cb95a7d418 | ||
![]() |
830f806dee | ||
![]() |
21c2316f6b | ||
![]() |
930a1ae450 | ||
![]() |
aec331b5a6 | ||
![]() |
1db6c281b1 | ||
![]() |
5b70ca81a7 | ||
![]() |
d00df785a5 | ||
![]() |
d0c6e7a285 | ||
![]() |
058995a0b6 | ||
![]() |
fd3be887b4 | ||
![]() |
84802d9065 | ||
![]() |
90d696d826 | ||
![]() |
aeb9e2ad9f | ||
![]() |
26ab1bfd4f | ||
![]() |
23d44cb28b | ||
![]() |
de4b352495 | ||
![]() |
d635117194 | ||
![]() |
c0412d368e | ||
![]() |
7ec30f0796 | ||
![]() |
9815a55221 | ||
![]() |
7c108e5128 | ||
![]() |
4873031c37 | ||
![]() |
15d23870c1 | ||
![]() |
2abbf7e762 | ||
![]() |
1578aa7cb6 | ||
![]() |
f2230751e7 | ||
![]() |
8fc0dda26d | ||
![]() |
0ab71badfa | ||
![]() |
0f24d924f9 | ||
![]() |
518a0de95f | ||
![]() |
fa5648c0c3 | ||
![]() |
bd620a764d | ||
![]() |
cff42b5d27 | ||
![]() |
25eef2a40e | ||
![]() |
db276d5f1f | ||
![]() |
ce0d906c35 | ||
![]() |
520826abc0 | ||
![]() |
5e1d6014d5 | ||
![]() |
32af17317c | ||
![]() |
3e7cc8a0a0 | ||
![]() |
f717ce52fa | ||
![]() |
d880f3c628 | ||
![]() |
0a97717b35 | ||
![]() |
029403ea10 | ||
![]() |
e4ec65622f | ||
![]() |
3ee3b97255 | ||
![]() |
a3dd6ce891 | ||
![]() |
0c97d7701e | ||
![]() |
b852b6f55c | ||
![]() |
d3b7b857a4 | ||
![]() |
63ad3f9290 | ||
![]() |
40cc30e054 | ||
![]() |
23374e98ae | ||
![]() |
40f544fffc | ||
![]() |
4fc69c9a2c | ||
![]() |
2e5a485804 | ||
![]() |
2d10b5be90 | ||
![]() |
b0d0bb7953 | ||
![]() |
8c69b85280 | ||
![]() |
9d6d1e4ff8 | ||
![]() |
e71c72b4a2 | ||
![]() |
bf4b29b3e3 | ||
![]() |
8e0809ceba | ||
![]() |
230af48899 | ||
![]() |
9dc459192e | ||
![]() |
8de2301c6b | ||
![]() |
f66b343edd | ||
![]() |
ff82d36e94 | ||
![]() |
c3e937f5e0 | ||
![]() |
25c5e823d9 | ||
![]() |
528b8b1271 | ||
![]() |
b88344a6c0 | ||
![]() |
9e135cc81d | ||
![]() |
72ef288357 | ||
![]() |
426ac29ca5 | ||
![]() |
ceb330e3e4 | ||
![]() |
7ea8f741f2 | ||
![]() |
443c9fba03 | ||
![]() |
0227f79bd7 | ||
![]() |
2812eaa13d | ||
![]() |
cb293fcdb3 | ||
![]() |
706428fe98 | ||
![]() |
698f5f951e | ||
![]() |
155c12f657 | ||
![]() |
a36c731d15 | ||
![]() |
6a793536dd | ||
![]() |
27532a4237 | ||
![]() |
467f69f50e | ||
![]() |
32e724e744 | ||
![]() |
5fb891c495 | ||
![]() |
d56cefdc7a | ||
![]() |
80c67d37c0 | ||
![]() |
1e29d9463e | ||
![]() |
75f6a53269 | ||
![]() |
b5a8f3156c | ||
![]() |
de245c08ce | ||
![]() |
76a7f25083 | ||
![]() |
d9a96fbb29 | ||
![]() |
16617660c3 | ||
![]() |
a83d9a075f | ||
![]() |
eca3fb1e19 | ||
![]() |
41bc799c3f | ||
![]() |
44726c7411 | ||
![]() |
8f7f9ec367 | ||
![]() |
21de8f2284 | ||
![]() |
15797a89e7 | ||
![]() |
c340a1d47f | ||
![]() |
71cec39bc4 | ||
![]() |
882542318c | ||
![]() |
7238ef4f55 | ||
![]() |
1049d65621 | ||
![]() |
552ae836e9 | ||
![]() |
1852c0a88c | ||
![]() |
d0d399bdfc | ||
![]() |
3975fe0f02 | ||
![]() |
0739cfc240 | ||
![]() |
f31bf4f7c9 | ||
![]() |
f413671915 | ||
![]() |
e67a210e95 | ||
![]() |
51dfa9a247 | ||
![]() |
7c121b8ba6 | ||
![]() |
ae92a5c25e | ||
![]() |
fabd3b8421 | ||
![]() |
94d754e63a | ||
![]() |
f869542a47 | ||
![]() |
2b616b688d | ||
![]() |
0925ea6f9c | ||
![]() |
87b5565679 | ||
![]() |
adcecf969a | ||
![]() |
f3831665d4 | ||
![]() |
9db8d44654 | ||
![]() |
e1075a3bbf | ||
![]() |
3b6f499fc5 | ||
![]() |
2c1fbe103b | ||
![]() |
1543862f3b | ||
![]() |
5b7b34a9f0 | ||
![]() |
fb6271eecf | ||
![]() |
031526b619 | ||
![]() |
e42ca2dbf2 | ||
![]() |
4f5816f42e | ||
![]() |
555d0d3378 | ||
![]() |
1495b756d3 | ||
![]() |
7f78d00b97 | ||
![]() |
bfd58b3cdf | ||
![]() |
e260b9473a | ||
![]() |
ccc56d446f | ||
![]() |
31a4e38bc0 | ||
![]() |
284366e6bb | ||
![]() |
9c6dda9bd2 | ||
![]() |
e5f6f8dfa7 | ||
![]() |
0232117de5 | ||
![]() |
96700fe3d1 | ||
![]() |
367a4e17a2 | ||
![]() |
eba0d7c1e0 | ||
![]() |
341a4a0d60 | ||
![]() |
d116424241 | ||
![]() |
db881ee011 | ||
![]() |
34bed44a9b | ||
![]() |
d431aa4b59 | ||
![]() |
2512bebc62 | ||
![]() |
309dbd7585 | ||
![]() |
a1f28caa8e | ||
![]() |
4204393337 | ||
![]() |
789e34702b | ||
![]() |
47bc1bf88d | ||
![]() |
9867227ccd | ||
![]() |
26302d478e | ||
![]() |
0decf94c9c | ||
![]() |
0ae78efd43 | ||
![]() |
b5af6b0bf9 | ||
![]() |
3eabefd3d6 | ||
![]() |
7ff6a6e0e7 | ||
![]() |
94dc611024 | ||
![]() |
0b28107432 | ||
![]() |
fe717dad7f | ||
![]() |
fb3816e2e5 | ||
![]() |
535bf4c848 | ||
![]() |
1fc60b0682 | ||
![]() |
302f1ee8e3 | ||
![]() |
42aec56b8e | ||
![]() |
199c00c59b | ||
![]() |
107bb14555 | ||
![]() |
5820792013 | ||
![]() |
0e20248494 | ||
![]() |
c4f4b247bc | ||
![]() |
0d66bc49c2 | ||
![]() |
bdc78a826f | ||
![]() |
6570062e61 | ||
![]() |
820facb833 | ||
![]() |
a933fcf7e2 | ||
![]() |
2d3039c6a2 | ||
![]() |
aca01044f4 | ||
![]() |
dffd1b1d69 | ||
![]() |
14fc64f2f1 | ||
![]() |
04791d06d0 | ||
![]() |
a485451328 | ||
![]() |
c7d399c122 | ||
![]() |
df155bed89 | ||
![]() |
3ee3630d22 | ||
![]() |
6b2911b8c8 | ||
![]() |
d474200d7f | ||
![]() |
cc6f6277f6 | ||
![]() |
0521e988bc | ||
![]() |
832ff39eb6 | ||
![]() |
134798e82f | ||
![]() |
b6f43966ea | ||
![]() |
1dab26bcbc | ||
![]() |
afa7be6fdc | ||
![]() |
36290c20aa | ||
![]() |
c33c7eb68e | ||
![]() |
5da33afecd | ||
![]() |
a49b49aaa9 | ||
![]() |
225fd0d05b | ||
![]() |
c43b4f9cf0 | ||
![]() |
3581f4c87e | ||
![]() |
7a4e0cc850 | ||
![]() |
ba49da75de | ||
![]() |
f329a5950e | ||
![]() |
d95158003c | ||
![]() |
1be6223a31 | ||
![]() |
76178b423e | ||
![]() |
195e537499 | ||
![]() |
418abc2b0a | ||
![]() |
277f0346f2 | ||
![]() |
6148cbb122 | ||
![]() |
3847bfc41a | ||
![]() |
783424dd26 | ||
![]() |
4c1861dd27 | ||
![]() |
7f6486c77d | ||
![]() |
1b2a50f9a3 | ||
![]() |
5dadf92a62 | ||
![]() |
76821addd7 | ||
![]() |
a769c55c72 | ||
![]() |
347ec91bfc | ||
![]() |
4245767357 | ||
![]() |
74d196ad50 | ||
![]() |
3c4649748d | ||
![]() |
22985900a8 | ||
![]() |
86300a8e3c | ||
![]() |
32ab2ae201 | ||
![]() |
23e4a2e28e | ||
![]() |
40515e62ac | ||
![]() |
b56490650c | ||
![]() |
b5751795b5 | ||
![]() |
43d6151506 | ||
![]() |
8ccfb606c0 | ||
![]() |
3b74cd5676 | ||
![]() |
26e766ee19 | ||
![]() |
6f96dc8f23 | ||
![]() |
1c3fdb73fb | ||
![]() |
332be7edd6 | ||
![]() |
76e0e935f0 | ||
![]() |
7e6eb65950 | ||
![]() |
b0a56a431b | ||
![]() |
166316e0c5 | ||
![]() |
4194e66d81 | ||
![]() |
c9b9d86892 | ||
![]() |
f2d00e6e42 | ||
![]() |
acfc166a11 | ||
![]() |
3255382132 | ||
![]() |
25831bfb60 | ||
![]() |
d011ca0626 | ||
![]() |
5eadbe7ecd | ||
![]() |
ea7e0c6204 | ||
![]() |
2dcb126e6d | ||
![]() |
438525e8ec | ||
![]() |
eb66a403d9 | ||
![]() |
26ac612474 | ||
![]() |
2e28df3e8a | ||
![]() |
43613e3b10 | ||
![]() |
555dd2e726 | ||
![]() |
3260f46543 | ||
![]() |
f9de23402a | ||
![]() |
9e155cf94a | ||
![]() |
dcace43ce2 | ||
![]() |
527e3d6cd0 | ||
![]() |
befce5b887 | ||
![]() |
9929c96650 | ||
![]() |
c7d45c28cf | ||
![]() |
87cc3fd714 | ||
![]() |
282d58a6fe | ||
![]() |
b4aec91d67 | ||
![]() |
d2a149ee23 | ||
![]() |
8a72f94b3d | ||
![]() |
fc4ed1c50b | ||
![]() |
ae1d0a18f3 | ||
![]() |
9709ca331e | ||
![]() |
b4a9641c31 | ||
![]() |
cc68e6b6e6 | ||
![]() |
60c47cfca1 | ||
![]() |
720abd4e04 | ||
![]() |
b4fe694d1b | ||
![]() |
c117785554 | ||
![]() |
a9d6a15924 | ||
![]() |
e9723de6c3 | ||
![]() |
b8c61b5236 | ||
![]() |
da0cf01eee | ||
![]() |
b77e1eb94b | ||
![]() |
e40f266042 | ||
![]() |
69fb15d3cb | ||
![]() |
188956a4af | ||
![]() |
c7dd4a7f2b | ||
![]() |
111f1729c9 | ||
![]() |
164372b46a | ||
![]() |
1af3794fed | ||
![]() |
885f4ded0c | ||
![]() |
9bb018d8d9 | ||
![]() |
58187e00e6 | ||
![]() |
5110a8c636 | ||
![]() |
ddbe883d47 | ||
![]() |
945ac82b6a | ||
![]() |
84148ad07a | ||
![]() |
25553a28a1 | ||
![]() |
c2f5846a8e | ||
![]() |
3f98e9451a | ||
![]() |
6c72194346 | ||
![]() |
822dd24756 | ||
![]() |
262ed38429 | ||
![]() |
5ff66ce4dd | ||
![]() |
ca73b8e0b8 | ||
![]() |
2d49ace0e3 | ||
![]() |
d52f22044e | ||
![]() |
2bf052aac9 | ||
![]() |
a79c9d5f4f | ||
![]() |
6f9bcf1858 | ||
![]() |
d1b00ba95d | ||
![]() |
bb45bee7f8 | ||
![]() |
246f0b21fd | ||
![]() |
394443b6d1 | ||
![]() |
fc1dd2daac | ||
![]() |
ba1b6f321e | ||
![]() |
06402cf1c6 | ||
![]() |
dfc33389d8 | ||
![]() |
4e83752655 | ||
![]() |
7e28718681 | ||
![]() |
bf452e922a | ||
![]() |
4911a74cac | ||
![]() |
a2bda5a4db | ||
![]() |
800abc605f | ||
![]() |
ac83b67632 | ||
![]() |
00bc7dccb9 | ||
![]() |
04aa9f0e61 | ||
![]() |
353e97a4c1 | ||
![]() |
7b28387bb4 | ||
![]() |
1889fe23c5 | ||
![]() |
9832fbe042 | ||
![]() |
084070475d | ||
![]() |
cb42ca8765 | ||
![]() |
45d7e66488 | ||
![]() |
93a1616933 | ||
![]() |
9a87a2ff0d | ||
![]() |
bdff05feaf | ||
![]() |
937e3d0b94 | ||
![]() |
8503aba583 | ||
![]() |
bb8223d4ff | ||
![]() |
af3c8f66f7 | ||
![]() |
3097a1b17e | ||
![]() |
54b80f74b2 | ||
![]() |
9a78d28cd7 | ||
![]() |
5df197e814 | ||
![]() |
5fe09e9787 | ||
![]() |
6be5360bdd | ||
![]() |
2b9007958b | ||
![]() |
2f9c126d34 | ||
![]() |
1ed817932b | ||
![]() |
0ccadb246f | ||
![]() |
b67235a7e5 | ||
![]() |
93853613bd | ||
![]() |
1ecb4fedcc | ||
![]() |
c8fdf3731a | ||
![]() |
f8e766ffc0 | ||
![]() |
050489dd80 | ||
![]() |
2db966ba47 | ||
![]() |
aa54d65f63 | ||
![]() |
37dc17ae15 | ||
![]() |
cd6b3d7dee | ||
![]() |
aec0bc5029 | ||
![]() |
e010e67717 | ||
![]() |
44197d91c6 | ||
![]() |
e3ee48b52e | ||
![]() |
8ccf349458 | ||
![]() |
2fce15db94 | ||
![]() |
0cf50e8000 | ||
![]() |
5b9630cf46 | ||
![]() |
7dcbb409a9 | ||
![]() |
286e057fae | ||
![]() |
7d4fe341f1 | ||
![]() |
76acb7bb58 | ||
![]() |
5ef7cd7bdd | ||
![]() |
ebb4628e66 | ||
![]() |
0928060c75 | ||
![]() |
d8f9a9a03c | ||
![]() |
abfb8bbc34 | ||
![]() |
82e01f7b17 | ||
![]() |
a97609e920 | ||
![]() |
f1e1bd41b9 | ||
![]() |
57d35858d8 | ||
![]() |
0b956c66c1 | ||
![]() |
b1dfa1db61 | ||
![]() |
cb579ecc62 | ||
![]() |
ae235aa58c | ||
![]() |
6302056182 | ||
![]() |
5e20abd7f1 | ||
![]() |
72a72dbc88 | ||
![]() |
4766666913 | ||
![]() |
fa6070c680 | ||
![]() |
6267d11e51 | ||
![]() |
822298ab66 | ||
![]() |
2ad3ab7f0d | ||
![]() |
f437fd6cd6 | ||
![]() |
42b247756a | ||
![]() |
fbbf2d5eb3 | ||
![]() |
788a276616 | ||
![]() |
196779ffb6 | ||
![]() |
59a144baed | ||
![]() |
ad825b80b0 | ||
![]() |
45a0061660 | ||
![]() |
8831c76fb4 | ||
![]() |
66616e1cab | ||
![]() |
fa281a0df0 | ||
![]() |
d48a96599f | ||
![]() |
7de7ef8e8c | ||
![]() |
406a254523 | ||
![]() |
eabd6f60ef | ||
![]() |
e416d06aa9 | ||
![]() |
65e073e6b8 | ||
![]() |
45ac0d5dc6 | ||
![]() |
c9ef034ab8 | ||
![]() |
c89f38f4f8 | ||
![]() |
858b57d77a | ||
![]() |
9b87892036 | ||
![]() |
ed69692f08 | ||
![]() |
7119999df8 | ||
![]() |
c16462a0ce | ||
![]() |
272f96b405 | ||
![]() |
3e519001a3 | ||
![]() |
f442aad962 | ||
![]() |
fa9b151c36 | ||
![]() |
481bd6f57a | ||
![]() |
f015227fc8 | ||
![]() |
61ba16b779 | ||
![]() |
f6e86bfcf8 | ||
![]() |
498dd76730 | ||
![]() |
ebd909dfe7 | ||
![]() |
067ed1258b | ||
![]() |
ef3c72a24f | ||
![]() |
b257f476c9 | ||
![]() |
d3af2be79a | ||
![]() |
94aeb19245 | ||
![]() |
e4ee392c27 | ||
![]() |
cf48ad06ed | ||
![]() |
63df85f7f9 | ||
![]() |
b29611509e | ||
![]() |
dd1cf0ec56 | ||
![]() |
c04c73bbd7 | ||
![]() |
0d287283d4 | ||
![]() |
4ea5723b7f | ||
![]() |
ea964124d6 | ||
![]() |
75244853c1 | ||
![]() |
c361c34b16 | ||
![]() |
264b3a785b | ||
![]() |
bb15e1d8ac | ||
![]() |
68860ae866 | ||
![]() |
61637f12c7 | ||
![]() |
7538649e81 | ||
![]() |
31efbe915e | ||
![]() |
9e69627626 | ||
![]() |
dd9da2853a | ||
![]() |
b4f08fa8d5 | ||
![]() |
670461c66f | ||
![]() |
f6092b9128 | ||
![]() |
a690cb36ff | ||
![]() |
5ca8f4a3aa | ||
![]() |
ba6e2f0a54 | ||
![]() |
149f4e38a0 | ||
![]() |
b5ee170726 | ||
![]() |
04961880fe | ||
![]() |
e22e8ffa0e | ||
![]() |
d18d800947 | ||
![]() |
3ca58ee65f | ||
![]() |
21387b9a83 | ||
![]() |
ccb0d491ed | ||
![]() |
daa3200713 | ||
![]() |
fec2d1f7ee | ||
![]() |
eb8e95723c | ||
![]() |
999bec19f1 | ||
![]() |
dfb8de2349 | ||
![]() |
59f1cdcc82 | ||
![]() |
3eb8be6239 | ||
![]() |
7dfc4a651d | ||
![]() |
189d145393 | ||
![]() |
26bec11d76 | ||
![]() |
1e7071bff3 | ||
![]() |
92390d1d59 | ||
![]() |
b9b072119b | ||
![]() |
b6d9fd8030 | ||
![]() |
d383bc93c7 | ||
![]() |
af88c68fca | ||
![]() |
24537c4fdc | ||
![]() |
130f495fb6 | ||
![]() |
9d22a06969 | ||
![]() |
e3783e0236 | ||
![]() |
a5161eb7f1 | ||
![]() |
82e448de7c | ||
![]() |
680afe972e | ||
![]() |
69ab2ce402 | ||
![]() |
4374930065 | ||
![]() |
9f3520cba5 | ||
![]() |
d20cdc099d | ||
![]() |
926e9ff3de | ||
![]() |
70c277d8a1 | ||
![]() |
0a5aac724a | ||
![]() |
bb91d5495f | ||
![]() |
995d94c124 | ||
![]() |
ee7036f13d | ||
![]() |
5a8a8dc292 | ||
![]() |
7d84de6690 | ||
![]() |
3520f9e9c2 | ||
![]() |
b2df7981a9 | ||
![]() |
9831e7bc3b | ||
![]() |
90e37439e5 | ||
![]() |
65f2490e89 | ||
![]() |
c14a921e5a | ||
![]() |
2bc8ffefce | ||
![]() |
b88d8c85cb | ||
![]() |
693f52d984 | ||
![]() |
56a3bff42e | ||
![]() |
bffeec1615 | ||
![]() |
e4a9f115cb | ||
![]() |
a717238f76 | ||
![]() |
4a567ab97c | ||
![]() |
90431b662b | ||
![]() |
1c8f349a62 | ||
![]() |
217004c70c | ||
![]() |
a75608628e | ||
![]() |
33130f39ee | ||
![]() |
cae43e97cd | ||
![]() |
cdfc0f6b71 | ||
![]() |
bf6a18a414 | ||
![]() |
16b66afa7a | ||
![]() |
9e446210fb | ||
![]() |
b0118d2d57 | ||
![]() |
bc90309dd6 | ||
![]() |
52c4093fb0 | ||
![]() |
b47c12cbee | ||
![]() |
fb14bc6016 | ||
![]() |
cd6862b1a7 | ||
![]() |
3d9160f2fa | ||
![]() |
157a5d9902 | ||
![]() |
f4972e2be2 | ||
![]() |
85c8e6ba42 | ||
![]() |
8e79bd8785 | ||
![]() |
1ace7ce254 | ||
![]() |
a00be5b60c | ||
![]() |
aabe8d1d5e | ||
![]() |
3da086438b | ||
![]() |
c257c86387 | ||
![]() |
ff3ae14c29 | ||
![]() |
cd82a34392 | ||
![]() |
baacd55910 | ||
![]() |
511b7c4d92 | ||
![]() |
f38851b7c6 | ||
![]() |
432c5837f0 | ||
![]() |
cc599f544a | ||
![]() |
e886c85134 | ||
![]() |
05eb716b85 | ||
![]() |
61eb7b0a6a | ||
![]() |
f0dade5856 | ||
![]() |
8567f6b13c | ||
![]() |
b740316918 | ||
![]() |
2119d08543 | ||
![]() |
599ff66522 | ||
![]() |
6d2eb04ada | ||
![]() |
0aea75edb7 | ||
![]() |
2ca8231ab4 | ||
![]() |
e00116551c | ||
![]() |
4d55e05b07 | ||
![]() |
9ff3f85432 | ||
![]() |
65c584aeda | ||
![]() |
2540a2dfd9 | ||
![]() |
70e9187bcb | ||
![]() |
5e2fa13471 | ||
![]() |
4f8610f895 | ||
![]() |
95f9e97af8 | ||
![]() |
695b073080 | ||
![]() |
3a28935bfe | ||
![]() |
468ed1a9ce | ||
![]() |
a6052d7900 | ||
![]() |
e42836b07f | ||
![]() |
16373919d4 | ||
![]() |
92f9c908f6 | ||
![]() |
480e1e17c8 | ||
![]() |
274f5f2f1f | ||
![]() |
02d4f9cbba | ||
![]() |
d10daf0991 | ||
![]() |
8c9656b799 | ||
![]() |
396ea3d0ee | ||
![]() |
3efee4a855 | ||
![]() |
ece6759fa7 | ||
![]() |
246d945f39 | ||
![]() |
4a3bc486d0 | ||
![]() |
996d041581 | ||
![]() |
d48e4adcd4 | ||
![]() |
7f84b7ab83 | ||
![]() |
3e26bd6a17 | ||
![]() |
e964c62907 | ||
![]() |
fbb4965263 | ||
![]() |
88139d95a7 | ||
![]() |
5875b1988b | ||
![]() |
2a2091595b | ||
![]() |
d77883dd7a | ||
![]() |
adea7dadec | ||
![]() |
cf4ce2dc08 | ||
![]() |
e74dfb2ba6 | ||
![]() |
aaea9ff018 | ||
![]() |
206b88eba2 | ||
![]() |
8f5373f034 | ||
![]() |
dfa0a16487 | ||
![]() |
8b9f6f49ef | ||
![]() |
bcac978810 | ||
![]() |
d17a41f7f1 | ||
![]() |
503f71f004 | ||
![]() |
4543288ea7 | ||
![]() |
9307105b3f | ||
![]() |
567daf9946 | ||
![]() |
558b22c36a | ||
![]() |
f076dddfe3 | ||
![]() |
af44730418 | ||
![]() |
4ec1140cb4 | ||
![]() |
2a5d20058f | ||
![]() |
51f38e0a76 | ||
![]() |
54eb42d658 | ||
![]() |
a7a6f4cec6 | ||
![]() |
3968a8e0dc | ||
![]() |
9e8ff27a7f | ||
![]() |
7dd3d2b040 | ||
![]() |
13678cb8d5 | ||
![]() |
890aa6f9ac | ||
![]() |
646cf44b83 | ||
![]() |
bf789ca97b | ||
![]() |
9b90ff10f4 | ||
![]() |
0cc719a823 | ||
![]() |
4cd026dfe9 | ||
![]() |
3120b49dba | ||
![]() |
fb1999c474 | ||
![]() |
2c37961f7b | ||
![]() |
1abc863f82 | ||
![]() |
c9be03c0bc | ||
![]() |
fd89209233 | ||
![]() |
2e362fbb64 | ||
![]() |
11b687cdc2 | ||
![]() |
747c920420 | ||
![]() |
cac51737cb | ||
![]() |
7789379914 | ||
![]() |
08e2bfe9a2 | ||
![]() |
56a854ec88 | ||
![]() |
de4ff4e58d | ||
![]() |
991c2afedb | ||
![]() |
cded92662f | ||
![]() |
2c3fa0fd8f | ||
![]() |
ec08432f92 | ||
![]() |
1bab4d6937 | ||
![]() |
d1fed11d0d | ||
![]() |
e96053e268 | ||
![]() |
9b98cbb894 | ||
![]() |
349578fb6e | ||
![]() |
7c186e4fcc | ||
![]() |
a4fcbb9f67 | ||
![]() |
763b64cc57 | ||
![]() |
d57e6c5bf2 | ||
![]() |
80711cc00a | ||
![]() |
8b61b9ebfe | ||
![]() |
e00e778bce | ||
![]() |
81e70925c4 | ||
![]() |
ddd32bd600 | ||
![]() |
2713573b9b | ||
![]() |
cf2e8bbc0b | ||
![]() |
bc56c1a0e1 | ||
![]() |
bfbf7af411 | ||
![]() |
c151221d12 | ||
![]() |
b8489724ef | ||
![]() |
7a2f556682 | ||
![]() |
92d883db87 | ||
![]() |
cb931d7af0 | ||
![]() |
6b54b57cb9 | ||
![]() |
1ca4348ca0 | ||
![]() |
3ca7006e3a | ||
![]() |
339d2a7bf3 | ||
![]() |
0d3debf9b9 | ||
![]() |
7f74a4f4b5 | ||
![]() |
220718cb58 | ||
![]() |
be2acbd3eb | ||
![]() |
9249c74b7b | ||
![]() |
2b87817ba2 | ||
![]() |
883c0f9dfe | ||
![]() |
3ffc58d442 | ||
![]() |
1bb4b0156f | ||
![]() |
92d2be3f5e | ||
![]() |
a7cded21f7 | ||
![]() |
74156b7ed8 | ||
![]() |
821f320347 | ||
![]() |
eee9274098 | ||
![]() |
8dd1f89225 | ||
![]() |
f2459ea904 | ||
![]() |
e51f7bfbff | ||
![]() |
e2215ced34 | ||
![]() |
b14c8e82a0 | ||
![]() |
e7d50d2614 | ||
![]() |
bca92883d2 | ||
![]() |
ed865e38de | ||
![]() |
dec45109d3 | ||
![]() |
48c267c5c1 | ||
![]() |
c041d39cab | ||
![]() |
52c18e77ae | ||
![]() |
52a189cdd2 | ||
![]() |
dca8a47da8 | ||
![]() |
6be9317fd7 | ||
![]() |
bee99ca8d0 | ||
![]() |
48dc89cf13 | ||
![]() |
fc7f609234 | ||
![]() |
0bed9b3c2e | ||
![]() |
31fa73518b | ||
![]() |
c6fd11157a | ||
![]() |
ca73a57de7 | ||
![]() |
38a545af92 | ||
![]() |
b91b340afd | ||
![]() |
8e7b1c97df | ||
![]() |
c9c55fe0c9 | ||
![]() |
d553160d65 | ||
![]() |
b8a4df2f50 | ||
![]() |
36edbf6ea9 | ||
![]() |
f966535ea9 | ||
![]() |
07cc26a144 | ||
![]() |
178c4d15b7 | ||
![]() |
b0b68c695c | ||
![]() |
219a3658d6 | ||
![]() |
d2310fc2ea | ||
![]() |
3fe0ccd934 | ||
![]() |
f902ebadcc | ||
![]() |
57323af167 | ||
![]() |
20b0b5fc8e | ||
![]() |
bb72b8cc93 | ||
![]() |
fec5516da9 | ||
![]() |
ec9a092751 | ||
![]() |
f78f8b6b12 | ||
![]() |
f97b79bc16 | ||
![]() |
42c8a77755 | ||
![]() |
c692cc6a70 | ||
![]() |
2b8d8d4e9c | ||
![]() |
2ae8032ace | ||
![]() |
c4416584de | ||
![]() |
86d8b49113 | ||
![]() |
7a5d870f67 | ||
![]() |
b43459232a | ||
![]() |
e2b4e60c9e | ||
![]() |
17543535e3 | ||
![]() |
901ce23cd2 | ||
![]() |
1b52d8065e | ||
![]() |
c6db901051 | ||
![]() |
39edb1ad37 | ||
![]() |
a397aa188c | ||
![]() |
dd08751f3f | ||
![]() |
ef6a9184ba | ||
![]() |
575fb6fc60 | ||
![]() |
4147d6c67e | ||
![]() |
115c17ab90 | ||
![]() |
d892c7290c | ||
![]() |
63d8a902d5 | ||
![]() |
43152fcf19 | ||
![]() |
1f135f1fa5 | ||
![]() |
c71d378eab | ||
![]() |
bb82bf762f | ||
![]() |
6fa5689aaf | ||
![]() |
d78bef72ea | ||
![]() |
e410de9552 | ||
![]() |
8dc262b415 | ||
![]() |
7fa494815f | ||
![]() |
929bb70e5a | ||
![]() |
36fb262fa0 | ||
![]() |
b8cc783583 | ||
![]() |
67b3d383e4 | ||
![]() |
9144ccac6b | ||
![]() |
04020d5ae2 | ||
![]() |
de4f1d09af | ||
![]() |
d16f9efeec | ||
![]() |
8ba5dfe447 | ||
![]() |
27d7f5f190 | ||
![]() |
c43c4c42c8 | ||
![]() |
3759071449 | ||
![]() |
613283f656 | ||
![]() |
506b6b51ce | ||
![]() |
c7eccfb714 | ||
![]() |
85d59945a0 | ||
![]() |
98be19b29f | ||
![]() |
4c8b490c89 | ||
![]() |
8dd1745149 | ||
![]() |
66406d86c1 | ||
![]() |
35832b07b9 | ||
![]() |
357683cbb9 | ||
![]() |
15bc6159f9 | ||
![]() |
7cdafa76a5 | ||
![]() |
e84b0c91f8 | ||
![]() |
cecffee3d3 | ||
![]() |
d039a40d7c | ||
![]() |
90463d8613 | ||
![]() |
90c965a6ba | ||
![]() |
e961691645 | ||
![]() |
6270d2408f | ||
![]() |
7ef1842237 | ||
![]() |
faf616cbea | ||
![]() |
4a09e18d9a | ||
![]() |
983aca515d | ||
![]() |
eb184d3c68 | ||
![]() |
187ecf511f | ||
![]() |
0ab35a3ca3 | ||
![]() |
8e208bc76d | ||
![]() |
1b4d92007e | ||
![]() |
4101d7664d | ||
![]() |
e128c3094a | ||
![]() |
95869eeb70 | ||
![]() |
d977bbd61c | ||
![]() |
a16a6211e2 | ||
![]() |
a75013e43e | ||
![]() |
1e157e95b6 | ||
![]() |
04dbfcd0e6 | ||
![]() |
73440e2287 | ||
![]() |
ee4459691f | ||
![]() |
379c6045b9 | ||
![]() |
00d0ddb4b5 | ||
![]() |
be08c01be6 | ||
![]() |
fb6abe34df | ||
![]() |
b0d107f916 | ||
![]() |
6d1ff7c3f3 | ||
![]() |
6f67132f4b | ||
![]() |
859d21162c | ||
![]() |
c739a7ea2f | ||
![]() |
7e48875fd4 | ||
![]() |
474e86306f | ||
![]() |
b436a660f1 | ||
![]() |
d36460e30b | ||
![]() |
0e236f9d88 | ||
![]() |
72eb61951b | ||
![]() |
22b2503839 | ||
![]() |
d7b5719dfd | ||
![]() |
4322d195d3 | ||
![]() |
4ecad65926 | ||
![]() |
f12025b86e | ||
![]() |
5cfea54b06 | ||
![]() |
c24b75953a | ||
![]() |
f3d52f4932 | ||
![]() |
ea6d339b9c | ||
![]() |
284a9999b3 | ||
![]() |
b241872a00 | ||
![]() |
b7034d3213 | ||
![]() |
0a0ced3e8e | ||
![]() |
e7449b4d56 | ||
![]() |
fab34c9e49 | ||
![]() |
7df08c2120 | ||
![]() |
b94e0ef797 | ||
![]() |
00fed4f995 | ||
![]() |
e8a7802e94 | ||
![]() |
bfbc1ebb13 | ||
![]() |
a802bb2736 | ||
![]() |
3c5838cafd | ||
![]() |
9217e8336d | ||
![]() |
13cfc2d7bc | ||
![]() |
171cebed1c | ||
![]() |
1dcef87e1c | ||
![]() |
23fb602a33 | ||
![]() |
a8a549e8fc | ||
![]() |
44790e99ea | ||
![]() |
a3f6717c59 | ||
![]() |
9587480e29 | ||
![]() |
c51dbae8b6 | ||
![]() |
8619e8fc0b | ||
![]() |
10dace6de6 | ||
![]() |
f6e843b5e2 | ||
![]() |
a7bad5df61 | ||
![]() |
f50c9e855c | ||
![]() |
3653666ffe | ||
![]() |
2123e43490 | ||
![]() |
19146d8012 | ||
![]() |
987dd36401 | ||
![]() |
6bb158786b | ||
![]() |
f66d091717 | ||
![]() |
0a3d911602 | ||
![]() |
e8edb99bb0 | ||
![]() |
917cd96a3d | ||
![]() |
20ad6d239d | ||
![]() |
eea3968bfb | ||
![]() |
7ac8f6dd19 | ||
![]() |
cafa78c3f3 | ||
![]() |
e808f7655c | ||
![]() |
4f28bf7ab4 | ||
![]() |
9380b2b4e8 | ||
![]() |
8caeddde15 | ||
![]() |
b637398a9c | ||
![]() |
2e5b19575f | ||
![]() |
102a69996a | ||
![]() |
37af48f3fb | ||
![]() |
f38747aa28 | ||
![]() |
88d05578a8 | ||
![]() |
b24c8b3410 | ||
![]() |
2c8dbb94fc | ||
![]() |
3da2ec5112 | ||
![]() |
c3545ef060 | ||
![]() |
4308a7cf79 | ||
![]() |
e629c90a71 | ||
![]() |
e7f07f5bfc | ||
![]() |
da7972c119 | ||
![]() |
efc476e50b | ||
![]() |
33f4e8cf73 | ||
![]() |
643f300792 | ||
![]() |
bd601ad2da | ||
![]() |
9f1a7a1c20 | ||
![]() |
bd721c1310 | ||
![]() |
98138a02e3 | ||
![]() |
f7004e7a7c | ||
![]() |
1db25d4b20 | ||
![]() |
507e198b70 | ||
![]() |
5c721ee435 | ||
![]() |
9a44f0c869 | ||
![]() |
8c5f9b4501 | ||
![]() |
713bf66a12 | ||
![]() |
78e9f5bd1a | ||
![]() |
f03abf2600 | ||
![]() |
d5eee5b56c | ||
![]() |
8899684092 | ||
![]() |
8e30e4925c | ||
![]() |
906c7a8ea1 | ||
![]() |
324fc149b3 | ||
![]() |
29b1f2afae | ||
![]() |
87fa14b281 | ||
![]() |
b6db86da50 | ||
![]() |
043947fdcb | ||
![]() |
713509527a | ||
![]() |
ae7eedf523 | ||
![]() |
0a1bd748d7 | ||
![]() |
1b351b7743 | ||
![]() |
e409bd298a | ||
![]() |
caef289b9b | ||
![]() |
84628bd9fc | ||
![]() |
43582b6319 | ||
![]() |
c14c4425a4 | ||
![]() |
c47d6cffa8 | ||
![]() |
f104fb0586 | ||
![]() |
75e410e4b4 | ||
![]() |
740316ae2b | ||
![]() |
b141ec7ea7 | ||
![]() |
50875d5759 | ||
![]() |
61b5e5ad7e | ||
![]() |
6403d18d15 | ||
![]() |
77f04d913a | ||
![]() |
c0a783198e | ||
![]() |
d370f67f60 | ||
![]() |
5f4199183c | ||
![]() |
d097f8bf1e | ||
![]() |
1e104bdd9e | ||
![]() |
8143be29d9 | ||
![]() |
1d8c9ac020 | ||
![]() |
7fcc8be73c | ||
![]() |
e5f9cfd5b6 | ||
![]() |
edd487c1f4 | ||
![]() |
71d84fecba | ||
![]() |
d30e219d7b | ||
![]() |
0cfda55b3a | ||
![]() |
17d1ca82a6 | ||
![]() |
81d2a67948 | ||
![]() |
3bae3aa9aa | ||
![]() |
7211e6b929 | ||
![]() |
82af52a0bc | ||
![]() |
e88d34ea19 | ||
![]() |
e1d7d9511f | ||
![]() |
22d7d2c1f6 | ||
![]() |
2b10a0c513 | ||
![]() |
6e59cfc66c | ||
![]() |
51d8344515 | ||
![]() |
535f285287 | ||
![]() |
779fd78091 | ||
![]() |
69a111ee35 | ||
![]() |
7288e5a8fd | ||
![]() |
ac57da8713 | ||
![]() |
2000d02768 | ||
![]() |
5ba1ebd525 | ||
![]() |
92329a8dd0 | ||
![]() |
21de073784 | ||
![]() |
3d502a41c5 | ||
![]() |
8570f9256d | ||
![]() |
fe09c417ff | ||
![]() |
ca891b0e70 | ||
![]() |
af3ee35c50 | ||
![]() |
ba0bc31525 | ||
![]() |
3905cad68d | ||
![]() |
30a029c19f | ||
![]() |
7cac609c06 | ||
![]() |
e1365f185c | ||
![]() |
c2ac215d43 | ||
![]() |
c47972d500 | ||
![]() |
3c36030977 | ||
![]() |
7b2836c63c | ||
![]() |
38e2fc6812 | ||
![]() |
b0037c75a3 | ||
![]() |
d7f6cd944c | ||
![]() |
0de928a674 | ||
![]() |
5c542d60e5 | ||
![]() |
b3a29188a2 | ||
![]() |
8417589789 | ||
![]() |
c982e022fe | ||
![]() |
a4c3fd4493 | ||
![]() |
7de35b4830 | ||
![]() |
3841780fe6 | ||
![]() |
2440d8e3d1 | ||
![]() |
5012418ed2 | ||
![]() |
a6ab4feca5 | ||
![]() |
cd0471386d | ||
![]() |
d6749d30ab | ||
![]() |
37a4a4dcdf | ||
![]() |
cebceb3ec3 | ||
![]() |
426635871b | ||
![]() |
701c90d18d | ||
![]() |
d00a708177 | ||
![]() |
1f32f96487 | ||
![]() |
7642f8d702 | ||
![]() |
e78bf240ca | ||
![]() |
4fb44d98db | ||
![]() |
113ee73609 | ||
![]() |
64441b39ac | ||
![]() |
b4e3cccf4b | ||
![]() |
6a3ef2a2e4 | ||
![]() |
1dd00c2ea9 | ||
![]() |
5038e4396e | ||
![]() |
bd025309fb | ||
![]() |
1ae0dd574d | ||
![]() |
7d5ce994ab | ||
![]() |
78e0c3dcca | ||
![]() |
edbb96bcd9 | ||
![]() |
bdb1e839ed | ||
![]() |
1880740561 | ||
![]() |
503835d47e | ||
![]() |
8d372bee67 | ||
![]() |
f385af595a | ||
![]() |
4cd6bafc15 | ||
![]() |
dc700c41fb | ||
![]() |
7cd9e8860d | ||
![]() |
b6adbc926f | ||
![]() |
c253464b2a | ||
![]() |
6306cfff96 | ||
![]() |
d86d88c7b7 | ||
![]() |
5f606bb0b7 | ||
![]() |
1daa97545b | ||
![]() |
d0189b0719 | ||
![]() |
222b92807e | ||
![]() |
de90e5b753 | ||
![]() |
ad4870a59b | ||
![]() |
9a6c387b8e | ||
![]() |
81b4a35076 | ||
![]() |
411b3d0888 | ||
![]() |
fc0dc4472b | ||
![]() |
7824f9ee63 | ||
![]() |
fb00bef05f | ||
![]() |
d7bc6b1be7 | ||
![]() |
79776ae8bc | ||
![]() |
90b3f009af | ||
![]() |
6f8deb83e3 | ||
![]() |
da02a68e60 | ||
![]() |
85fc8710dd | ||
![]() |
a01f3e4efd | ||
![]() |
cce54f3bb7 | ||
![]() |
92037a480d | ||
![]() |
cf6005ce2f | ||
![]() |
a766ddbebc | ||
![]() |
01acb08c83 | ||
![]() |
023c259034 | ||
![]() |
dfe4d70198 | ||
![]() |
4d939cb778 | ||
![]() |
660a809ec8 | ||
![]() |
26825ed60f | ||
![]() |
8bd8975e0a | ||
![]() |
799d4f9465 | ||
![]() |
47797bcf4a | ||
![]() |
2847921e5a | ||
![]() |
8402de601f | ||
![]() |
4dec75c589 | ||
![]() |
ac15b227da | ||
![]() |
a1dc7a52e9 | ||
![]() |
6ba3e25d33 | ||
![]() |
51a2fa47c9 | ||
![]() |
09bd6dba84 | ||
![]() |
925bc3b3dd | ||
![]() |
382bf1e94c | ||
![]() |
575a7531c5 | ||
![]() |
dce0c0b47e | ||
![]() |
dc8230bf2e | ||
![]() |
17428d58d9 | ||
![]() |
6771bd84c6 | ||
![]() |
ac10b3a5c7 | ||
![]() |
56f2513aba | ||
![]() |
9858a009da | ||
![]() |
73b818cf78 | ||
![]() |
4f8ffaee5b | ||
![]() |
2cd40c7552 | ||
![]() |
c9fd69ceb7 | ||
![]() |
e4fbf70568 | ||
![]() |
cf93d6bb65 | ||
![]() |
228890aa19 | ||
![]() |
ad8512e246 | ||
![]() |
4480abc646 | ||
![]() |
94243ac605 | ||
![]() |
e8942c2968 | ||
![]() |
8e03395b53 | ||
![]() |
4f3de8646a | ||
![]() |
6802f5a802 | ||
![]() |
3a1ddce13f | ||
![]() |
6c18857109 | ||
![]() |
508b1e76c2 | ||
![]() |
51d3268eed | ||
![]() |
a0c90d4ba3 | ||
![]() |
2c27240da6 | ||
![]() |
787f5f06df | ||
![]() |
7894389f1d | ||
![]() |
da5f6315be | ||
![]() |
42d1ab8a87 | ||
![]() |
cf51892782 | ||
![]() |
cdd4319991 | ||
![]() |
3287eef0f1 | ||
![]() |
c4a261f2d2 | ||
![]() |
1867db3c4b | ||
![]() |
9e23d52193 | ||
![]() |
e29be737f7 | ||
![]() |
3ac4122ec8 | ||
![]() |
d56a79e016 | ||
![]() |
77ace76e32 | ||
![]() |
3270605b4f | ||
![]() |
36e10539e0 | ||
![]() |
37ba649930 | ||
![]() |
586e72e797 | ||
![]() |
38bd0d6bbb | ||
![]() |
da0da4c75e | ||
![]() |
bfe16bb254 | ||
![]() |
2ada88a266 | ||
![]() |
5b51ce9840 | ||
![]() |
26793c8428 | ||
![]() |
8719adef1e | ||
![]() |
cd7bcb79e5 | ||
![]() |
005419ab9a | ||
![]() |
d28d2c57e4 | ||
![]() |
64970772c7 | ||
![]() |
6019b7bda4 | ||
![]() |
5c792eb869 | ||
![]() |
c6126db2a2 | ||
![]() |
030833f942 | ||
![]() |
daf67d9cf7 | ||
![]() |
301ed25019 | ||
![]() |
a7dbfc4954 | ||
![]() |
af5e38e5ee | ||
![]() |
102a28aef4 | ||
![]() |
d3d8fdff13 | ||
![]() |
9cb6f95420 | ||
![]() |
89f334e67b | ||
![]() |
b8b3d17570 | ||
![]() |
053083f600 | ||
![]() |
cf46564c67 | ||
![]() |
59b038b8c4 | ||
![]() |
fb6dfcd3fc | ||
![]() |
1711ac9a88 | ||
![]() |
e14c5bbd25 | ||
![]() |
cf0f0d77be | ||
![]() |
be7e7ac274 | ||
![]() |
844bef9fda | ||
![]() |
d56d41a8c2 | ||
![]() |
b722c618bd | ||
![]() |
720fae8720 | ||
![]() |
8744b40dc5 | ||
![]() |
d053bc562f | ||
![]() |
cade1800f4 | ||
![]() |
450a0180f8 | ||
![]() |
ea363a8764 | ||
![]() |
bbc8049832 | ||
![]() |
66acd60406 | ||
![]() |
6fd8a5a084 | ||
![]() |
5927332a5f | ||
![]() |
44a4b11d36 | ||
![]() |
b8db47e528 | ||
![]() |
f7652bd2ef | ||
![]() |
e9f6509cb0 | ||
![]() |
3c57e2e2b9 | ||
![]() |
753ae51dd5 | ||
![]() |
638178556f | ||
![]() |
b44ed70112 | ||
![]() |
3e494cc551 | ||
![]() |
fa55896722 | ||
![]() |
67a51be9ee | ||
![]() |
2149bbb8ea | ||
![]() |
d92a93f1d5 | ||
![]() |
991fad02d7 | ||
![]() |
7497e43fb9 | ||
![]() |
1336de12a0 | ||
![]() |
0f78bd3785 | ||
![]() |
90b567c721 | ||
![]() |
8176f1141e | ||
![]() |
515827c745 | ||
![]() |
1a2701561b | ||
![]() |
c7d3ee0612 | ||
![]() |
4be3cb4b0d | ||
![]() |
db759bef46 | ||
![]() |
3d47a8e9c0 | ||
![]() |
91fe80334b | ||
![]() |
7125d19af1 | ||
![]() |
873e41dcfb | ||
![]() |
420f858d9b | ||
![]() |
e6f74751d4 | ||
![]() |
5a4713950c | ||
![]() |
c6691564a5 | ||
![]() |
8acb44b202 | ||
![]() |
72770e5809 | ||
![]() |
968721a515 | ||
![]() |
1de009a80d | ||
![]() |
74b6a78a9b | ||
![]() |
32fc6d4b7f | ||
![]() |
d05b49496c | ||
![]() |
52e5f467b0 | ||
![]() |
4e0cf380be | ||
![]() |
ebe9499e97 | ||
![]() |
1cb3ab82c2 | ||
![]() |
e0a954caa2 | ||
![]() |
ecce3243de | ||
![]() |
5e6532959b | ||
![]() |
d8b7cfb36b | ||
![]() |
b2837563c4 | ||
![]() |
957948f906 | ||
![]() |
7befafe7b7 | ||
![]() |
436ce63e30 | ||
![]() |
0268beb9c2 | ||
![]() |
6a42e477aa | ||
![]() |
22bc3dac2d | ||
![]() |
4db4329ce3 | ||
![]() |
924073424f | ||
![]() |
9e828eccf6 | ||
![]() |
3a8c99d43c | ||
![]() |
acb17dc575 | ||
![]() |
58987989da | ||
![]() |
86e8ee334f | ||
![]() |
998f2f9421 | ||
![]() |
8694e7a6bf | ||
![]() |
b46a6ed094 | ||
![]() |
d0f53cb14a | ||
![]() |
96d497a5cd | ||
![]() |
a8785570df | ||
![]() |
aa36e04250 | ||
![]() |
821b7ed9af | ||
![]() |
9270f80af2 | ||
![]() |
eb8710df81 | ||
![]() |
095099a085 | ||
![]() |
ad424cac18 | ||
![]() |
4e11806d00 | ||
![]() |
f45082b764 | ||
![]() |
cdca715afc | ||
![]() |
d0a08a55d1 | ||
![]() |
c5f3e76b21 | ||
![]() |
f58ff68f3c | ||
![]() |
a098aa0a89 | ||
![]() |
7b3fc206f7 | ||
![]() |
8f5d2a7f00 | ||
![]() |
44c585e8c8 | ||
![]() |
640f45f57d | ||
![]() |
0ba8bace0d | ||
![]() |
4202fb8cdc | ||
![]() |
5f8b8835e1 | ||
![]() |
78edcfe5cf | ||
![]() |
2ca5e0c8b8 | ||
![]() |
7ca1d99412 | ||
![]() |
118bcbd6a6 | ||
![]() |
987b442796 | ||
![]() |
8ea77ccd04 | ||
![]() |
004c2920f5 | ||
![]() |
f02616acd1 | ||
![]() |
69f9a484ca | ||
![]() |
dde5367560 | ||
![]() |
66da57f375 | ||
![]() |
d328db543e | ||
![]() |
c2c46f2843 | ||
![]() |
96da48ae82 | ||
![]() |
eb9eda1f6d | ||
![]() |
f16558c126 | ||
![]() |
1abf0fc134 | ||
![]() |
b0ba1b2ecc | ||
![]() |
a70717f2f7 | ||
![]() |
5dec73f27c | ||
![]() |
92ac87fa3f | ||
![]() |
267239e3f2 | ||
![]() |
52b863fe36 | ||
![]() |
79337a4568 | ||
![]() |
84750e2605 | ||
![]() |
dfa1a3ad90 | ||
![]() |
c1f9445e9d | ||
![]() |
191db46c4f | ||
![]() |
bc67bf6826 | ||
![]() |
df534d30bf | ||
![]() |
ce99dbe40c | ||
![]() |
affcca8034 | ||
![]() |
a6f31a577a | ||
![]() |
9b814f4ed8 | ||
![]() |
3091440162 | ||
![]() |
3b2573afed | ||
![]() |
1b64747c2b | ||
![]() |
863212915c | ||
![]() |
8ef82ebdd7 | ||
![]() |
78aad924c5 | ||
![]() |
24fda89665 | ||
![]() |
59cb9ba344 | ||
![]() |
d511eb19ef | ||
![]() |
d3e9f51d6b | ||
![]() |
e3499e5a70 | ||
![]() |
026425407e | ||
![]() |
18884111a6 | ||
![]() |
3e5b152718 | ||
![]() |
8e9637f6d4 | ||
![]() |
d11ad39048 | ||
![]() |
cb44d45eeb | ||
![]() |
c15ffca48c | ||
![]() |
902c11d074 | ||
![]() |
35250677b9 | ||
![]() |
1f3a1a0a95 | ||
![]() |
38330b735c | ||
![]() |
bda3c4ab7a | ||
![]() |
d8d01ac353 | ||
![]() |
51d935f419 | ||
![]() |
c96f83b076 | ||
![]() |
2fe4edb6df | ||
![]() |
7308f5993c | ||
![]() |
1e4f8be74b | ||
![]() |
22ac12dc36 | ||
![]() |
866296fefd | ||
![]() |
9d574397bc | ||
![]() |
b6e53b1a0c | ||
![]() |
a46894f395 | ||
![]() |
bee48dae7e | ||
![]() |
e12f7b67e5 | ||
![]() |
c7ceeb1833 | ||
![]() |
12f6ed621e | ||
![]() |
d7daada42e | ||
![]() |
6c6849d8d7 | ||
![]() |
e82336f355 | ||
![]() |
1151b615a0 | ||
![]() |
0cf891b9d9 | ||
![]() |
aeb8fd6fda | ||
![]() |
615f807909 | ||
![]() |
985255afed | ||
![]() |
9bec394d7f | ||
![]() |
8f72e0ab83 | ||
![]() |
6799a3bd35 | ||
![]() |
588c7a81fc | ||
![]() |
ce32ac19dd | ||
![]() |
16aa0e4dba | ||
![]() |
81cb765b7a | ||
![]() |
d5523615ef | ||
![]() |
ff5d899a92 | ||
![]() |
d9f2c2b968 | ||
![]() |
2f6adbd07c | ||
![]() |
f0992d56c1 | ||
![]() |
67bcf42125 | ||
![]() |
935ddd12ec | ||
![]() |
5a772f5410 | ||
![]() |
698a32f364 | ||
![]() |
bdea2acf67 | ||
![]() |
377a11b160 | ||
![]() |
9519c8edbd | ||
![]() |
64fd207533 | ||
![]() |
96e04f1c54 | ||
![]() |
348c4b4431 | ||
![]() |
bfbff723ac | ||
![]() |
03dba4d7d0 | ||
![]() |
febd7cbca8 | ||
![]() |
e2b7b228c5 | ||
![]() |
8b58d96aea | ||
![]() |
0a2e01a616 | ||
![]() |
a8908238d5 | ||
![]() |
569138ac80 | ||
![]() |
cf3faa1170 | ||
![]() |
ae50374d55 | ||
![]() |
d2705f033d | ||
![]() |
5d12e2291c | ||
![]() |
cd46d84dcb | ||
![]() |
a3b0b8b3d0 | ||
![]() |
4c60a70c6f | ||
![]() |
a75b3a53aa | ||
![]() |
94a79b57e9 | ||
![]() |
dbe90eb3f5 | ||
![]() |
3891348c26 | ||
![]() |
84106d6282 | ||
![]() |
37395931bf | ||
![]() |
88e487be8e | ||
![]() |
fd4c0e795a | ||
![]() |
c79eb43347 | ||
![]() |
860e74bffa | ||
![]() |
4c759083be | ||
![]() |
d3bb079be4 | ||
![]() |
ed374684a6 | ||
![]() |
169a46c38c | ||
![]() |
1dbea434a3 | ||
![]() |
a5b4d7a244 | ||
![]() |
7ebb58b1fa | ||
![]() |
ac5b3110f2 | ||
![]() |
48259b4ffe | ||
![]() |
041426fecb | ||
![]() |
2caf7efdc6 | ||
![]() |
b00e038b33 | ||
![]() |
36503f0417 | ||
![]() |
0daddf9896 | ||
![]() |
f3364e1327 | ||
![]() |
cf54115077 | ||
![]() |
12a94c50b8 | ||
![]() |
0dd12cf0a6 | ||
![]() |
f92c336ae4 | ||
![]() |
477c9ef577 | ||
![]() |
e146a476bd | ||
![]() |
45e74126da | ||
![]() |
f32a39e79f | ||
![]() |
489b144085 | ||
![]() |
e7d311d23c | ||
![]() |
4cc2dea2fe | ||
![]() |
1e2cae7087 | ||
![]() |
2b259f3704 | ||
![]() |
0df5221784 | ||
![]() |
4c78d35680 | ||
![]() |
cdef8472e3 | ||
![]() |
f9c933bf3b | ||
![]() |
cc995ecef8 | ||
![]() |
922a3631ae | ||
![]() |
0903078618 | ||
![]() |
8c759cb65a | ||
![]() |
bd45c67528 | ||
![]() |
562a206508 | ||
![]() |
dd584e929f | ||
![]() |
1828a2a81c | ||
![]() |
a6746f77f0 | ||
![]() |
184ff7a3b3 | ||
![]() |
9db872db56 | ||
![]() |
f1a1b89d13 | ||
![]() |
14e0e96596 | ||
![]() |
793c878c66 | ||
![]() |
14887d5e88 | ||
![]() |
366006273d | ||
![]() |
6a69a726f2 | ||
![]() |
e9cd4d95a7 | ||
![]() |
d7e1dc1f95 | ||
![]() |
19c8b4fe2d | ||
![]() |
8b73a7375a | ||
![]() |
974798f79c | ||
![]() |
43c5de074f | ||
![]() |
461cc1d5a8 | ||
![]() |
3d6df07335 | ||
![]() |
7689587879 | ||
![]() |
e248b989e1 | ||
![]() |
9920424a31 | ||
![]() |
17be0a2c12 | ||
![]() |
26fa38d052 | ||
![]() |
34a4382920 | ||
![]() |
e7c9b5a4f0 | ||
![]() |
8c31904838 | ||
![]() |
d522b608d3 | ||
![]() |
3e7303c15c | ||
![]() |
6e342983f9 | ||
![]() |
a6d7ed3824 | ||
![]() |
e003ec39fb | ||
![]() |
62abc63f7a | ||
![]() |
0955480f5b | ||
![]() |
bf76affd06 | ||
![]() |
b4d6525899 | ||
![]() |
ba3555a66f | ||
![]() |
d8be286831 | ||
![]() |
65f5bc531c | ||
![]() |
3ba4702181 | ||
![]() |
ad9c42e832 | ||
![]() |
0368d3be59 | ||
![]() |
598549b5fb | ||
![]() |
49086a3ae0 | ||
![]() |
4676a7aa62 | ||
![]() |
7d25b234d5 | ||
![]() |
c6578870f0 | ||
![]() |
4e8030bd81 | ||
![]() |
094e130544 | ||
![]() |
879fd5a13d | ||
![]() |
c2bb49aca0 | ||
![]() |
9ed14b6e8c | ||
![]() |
4484721a9f | ||
![]() |
4963a5797b | ||
![]() |
13f5798ed4 | ||
![]() |
db8e1dec3e | ||
![]() |
c9bdecdb69 | ||
![]() |
adb7726974 | ||
![]() |
d6883d0c1c | ||
![]() |
b2409ac708 | ||
![]() |
cf189663a7 | ||
![]() |
f0d36fd769 | ||
![]() |
213e3e998a | ||
![]() |
bef9c68f81 | ||
![]() |
099d2fd03d | ||
![]() |
9f71a6ab18 | ||
![]() |
e088f7a552 | ||
![]() |
2e6ce0481c | ||
![]() |
23d39e5366 | ||
![]() |
279664a578 | ||
![]() |
2a3d128f3c | ||
![]() |
9667365d9e | ||
![]() |
d4ef26e0f3 | ||
![]() |
a053e81797 | ||
![]() |
1494bfe855 | ||
![]() |
f1a4ba4e76 | ||
![]() |
385c526414 | ||
![]() |
5e95e02429 | ||
![]() |
3081802b1c | ||
![]() |
1663601dcb | ||
![]() |
1a513c7bbf | ||
![]() |
92cbdde429 | ||
![]() |
0f6b1e4ae1 | ||
![]() |
f915201a27 | ||
![]() |
d1f76042e1 | ||
![]() |
845aa733b7 | ||
![]() |
a0e318827d | ||
![]() |
39720a11dc | ||
![]() |
da33bf3438 | ||
![]() |
eda6f134a9 | ||
![]() |
46e1c8ef73 | ||
![]() |
f247139f1c | ||
![]() |
566985cf72 | ||
![]() |
70b4cdf520 | ||
![]() |
081f211231 | ||
![]() |
9cd8acab43 | ||
![]() |
8b79a9708b | ||
![]() |
969acbd47f | ||
![]() |
31012671c5 | ||
![]() |
e362b3b6aa | ||
![]() |
d2ced93e58 | ||
![]() |
958cd35e21 | ||
![]() |
73e898773b | ||
![]() |
46eae410c3 | ||
![]() |
73a9e1c316 | ||
![]() |
03862d4b6c | ||
![]() |
557a2f08f7 | ||
![]() |
ae90aa4ccc | ||
![]() |
6eff2e6e74 | ||
![]() |
955bb51f11 | ||
![]() |
b583def913 | ||
![]() |
dd85da053f | ||
![]() |
ba6a01408d | ||
![]() |
81d880fb4e | ||
![]() |
bdbce67473 | ||
![]() |
6079effae3 | ||
![]() |
41aa1ee318 | ||
![]() |
8d2fe315db | ||
![]() |
22c3ac5be3 | ||
![]() |
a517f0df1d | ||
![]() |
c14f16bdf1 | ||
![]() |
9c9854b6bf | ||
![]() |
d280e16723 | ||
![]() |
b93a9cb8bc | ||
![]() |
8601052179 | ||
![]() |
eaa20ff4bf | ||
![]() |
e4c6e4bc48 | ||
![]() |
c52597205e | ||
![]() |
eee9de8271 | ||
![]() |
c73033c0b4 | ||
![]() |
aecfa3ff0f | ||
![]() |
18f575bee4 | ||
![]() |
522e752582 | ||
![]() |
854ed8cfa9 | ||
![]() |
4642205768 | ||
![]() |
40dbfbe092 | ||
![]() |
7882519c0e | ||
![]() |
0234017ca1 | ||
![]() |
6c315e5a9c | ||
![]() |
ef0f91d8d0 | ||
![]() |
9552784e72 | ||
![]() |
ac20d7f302 | ||
![]() |
f068327307 | ||
![]() |
1bc698ae78 | ||
![]() |
3f6691dcd5 | ||
![]() |
31981b9080 | ||
![]() |
167d29c39f | ||
![]() |
28b78c2b27 | ||
![]() |
b3d691fff6 | ||
![]() |
8e5f2d91e8 | ||
![]() |
cfa7f60e5f | ||
![]() |
e0fcaa6103 | ||
![]() |
1b2134d7a8 | ||
![]() |
f922268af7 | ||
![]() |
841a9f8082 | ||
![]() |
4865ddb377 | ||
![]() |
a556732e4f | ||
![]() |
0ea31cb088 | ||
![]() |
06fe7323eb | ||
![]() |
d8d9259c36 | ||
![]() |
261260b232 | ||
![]() |
5b88894638 | ||
![]() |
ddadaa828a | ||
![]() |
a3cd10d83b | ||
![]() |
b626c7620e | ||
![]() |
5d90d8930e | ||
![]() |
b01309c3bf | ||
![]() |
2d1511b5dd | ||
![]() |
e561304645 | ||
![]() |
961d5591bd | ||
![]() |
eca3f12fed | ||
![]() |
a2c8796e04 | ||
![]() |
ad301fd087 | ||
![]() |
02b08939cd | ||
![]() |
9b0d583f1b | ||
![]() |
4a0a07f158 | ||
![]() |
9c864c9759 | ||
![]() |
f1dd1bd6bd | ||
![]() |
85b1c309d1 | ||
![]() |
6fe43b7b5c | ||
![]() |
62fc986d96 | ||
![]() |
03710f1fd2 | ||
![]() |
2283c7a926 | ||
![]() |
fb19ca8bf4 | ||
![]() |
4ebfc1516c | ||
![]() |
cc713e6c89 | ||
![]() |
1be65adf02 | ||
![]() |
25427ee60d | ||
![]() |
c8eefbaa2e | ||
![]() |
b339f426f2 | ||
![]() |
9fd26fa574 | ||
![]() |
be90bf0188 | ||
![]() |
adcdaba199 | ||
![]() |
17907589cc | ||
![]() |
f333df181f | ||
![]() |
61a01cb163 | ||
![]() |
4ce557a829 | ||
![]() |
4d10c9de95 | ||
![]() |
5b84acebbc | ||
![]() |
fc845dc936 | ||
![]() |
7beae93441 | ||
![]() |
c8f1297adb | ||
![]() |
053a1d34e5 | ||
![]() |
110a75ba0b | ||
![]() |
826a31e57d | ||
![]() |
23d7c3d0fe | ||
![]() |
4d4a20e05e | ||
![]() |
c03d4f115f | ||
![]() |
ed90b638a9 | ||
![]() |
c5252e06a7 | ||
![]() |
8af445e72b | ||
![]() |
94a0199955 | ||
![]() |
44739c5198 | ||
![]() |
5f871bc01f | ||
![]() |
b382dd6fc0 | ||
![]() |
2e84f82ed6 | ||
![]() |
1f5971f15a | ||
![]() |
694466a196 | ||
![]() |
03311d3776 | ||
![]() |
ae0eba866a | ||
![]() |
05f92b74e7 | ||
![]() |
41b6f3ffa7 | ||
![]() |
c895b76864 | ||
![]() |
906737bedb | ||
![]() |
7138e891be | ||
![]() |
53abe36b83 | ||
![]() |
efbb7a034c | ||
![]() |
d7dac57a07 | ||
![]() |
05bc81bf4e | ||
![]() |
59ce88f044 | ||
![]() |
f8bc0bd2b5 | ||
![]() |
7ebb184c8a | ||
![]() |
bd44205b4e | ||
![]() |
cf94cb1092 | ||
![]() |
02d92e32c7 | ||
![]() |
68087cdea5 | ||
![]() |
7f92607b85 | ||
![]() |
3be4b69b44 | ||
![]() |
bb9afcb304 | ||
![]() |
e9a05890a5 | ||
![]() |
613809c2af | ||
![]() |
7b969bb8c2 | ||
![]() |
7aef551292 | ||
![]() |
447b811fa0 | ||
![]() |
5151aa677f | ||
![]() |
a5ff34d423 | ||
![]() |
435040814d | ||
![]() |
9987416a4a | ||
![]() |
31e33e0a8b | ||
![]() |
03cfae45f8 | ||
![]() |
4f83325e3c | ||
![]() |
cd1765a0f3 | ||
![]() |
5c744ad9aa | ||
![]() |
b211d8b085 | ||
![]() |
f8eece362f | ||
![]() |
d4ea30e081 | ||
![]() |
83416ee2e0 | ||
![]() |
fa981a389f | ||
![]() |
25915b5521 | ||
![]() |
ed0dcb5c3d | ||
![]() |
55817f31f9 | ||
![]() |
a5b19bbc83 | ||
![]() |
f6b44e03ac | ||
![]() |
6d2ef4e0bf | ||
![]() |
4d714cf9a4 | ||
![]() |
930ded6767 | ||
![]() |
4cdb18907f | ||
![]() |
93bccb96b6 | ||
![]() |
38bc618ee5 | ||
![]() |
00b0193a43 | ||
![]() |
e2db37d28a | ||
![]() |
f9bce54104 | ||
![]() |
7ee14724fc | ||
![]() |
5988c2ac78 | ||
![]() |
4eb0dbb5a4 | ||
![]() |
8c5b3fe23e | ||
![]() |
e35ad7551b | ||
![]() |
6c943a7158 | ||
![]() |
2da14e5b2a | ||
![]() |
97f8eea302 | ||
![]() |
04d5932252 | ||
![]() |
84e38f765d | ||
![]() |
b33c5798ee | ||
![]() |
81012e60ff | ||
![]() |
6180c2f948 | ||
![]() |
52f0ae9350 | ||
![]() |
fa0936da3c | ||
![]() |
8a30c6347e | ||
![]() |
09bcf34050 | ||
![]() |
795c515999 | ||
![]() |
00dbdc2267 | ||
![]() |
32286888e5 | ||
![]() |
565d8d8f04 | ||
![]() |
0a5a0bef48 | ||
![]() |
6e0e5c102e | ||
![]() |
d31271fee3 | ||
![]() |
be8a9ae73b | ||
![]() |
afaa001738 | ||
![]() |
22fbb0e35b | ||
![]() |
026a53f3ff | ||
![]() |
e17203ca1b | ||
![]() |
599c7919ce | ||
![]() |
778b601cd5 | ||
![]() |
3170fa2208 | ||
![]() |
07216db864 | ||
![]() |
fec870f264 | ||
![]() |
4518f089cc | ||
![]() |
2c5eba335f | ||
![]() |
fb19f1ecbc | ||
![]() |
91dd03ba67 | ||
![]() |
86092541ed | ||
![]() |
e879fe5843 | ||
![]() |
0ca7699fe5 | ||
![]() |
7f6adfa331 | ||
![]() |
5f0b102671 | ||
![]() |
70a59daa6f | ||
![]() |
d28eb6ae21 | ||
![]() |
34865f797f | ||
![]() |
eca980dfca | ||
![]() |
742c792ec7 | ||
![]() |
24a5f7a38e | ||
![]() |
554949102b | ||
![]() |
0f3b95802d | ||
![]() |
868910fddf | ||
![]() |
436542eff8 | ||
![]() |
f4c05c67a2 | ||
![]() |
6f7f67df5a | ||
![]() |
50b6163e73 | ||
![]() |
9b062f33c5 | ||
![]() |
ea15c2245e | ||
![]() |
beb539abaf | ||
![]() |
6dd1d45a02 | ||
![]() |
26ae6d3691 | ||
![]() |
90ea01aa46 | ||
![]() |
6c52105ac7 | ||
![]() |
f97bc9dba8 | ||
![]() |
3332375d7c | ||
![]() |
fe6b1c13c4 | ||
![]() |
5608425a12 | ||
![]() |
f784b01d20 | ||
![]() |
2648eba5bf | ||
![]() |
255347ab77 | ||
![]() |
52c36ef6a4 | ||
![]() |
ed2e083d13 | ||
![]() |
80f7c5ed9d | ||
![]() |
8d33cbeaca | ||
![]() |
b0b8bc7385 | ||
![]() |
edbc8b28d5 | ||
![]() |
da9bab16e1 | ||
![]() |
75098d8b3e | ||
![]() |
65bb7fd533 | ||
![]() |
0259e78b2f | ||
![]() |
99b8888a1b | ||
![]() |
689f4ef606 | ||
![]() |
b0c40e1e37 | ||
![]() |
e54819e7e5 | ||
![]() |
7eb029dcb6 | ||
![]() |
3da70c3e8b | ||
![]() |
f8c80283e4 | ||
![]() |
72a6681ac1 | ||
![]() |
1ab555b590 | ||
![]() |
4602ec7688 | ||
![]() |
5c39d8d12e | ||
![]() |
97284fcf87 | ||
![]() |
c8b1654e0a | ||
![]() |
0400d0e0f0 | ||
![]() |
4040f6bec6 | ||
![]() |
a4a3aa045b | ||
![]() |
a5a18903c7 | ||
![]() |
6990986d9f | ||
![]() |
06ded0098c | ||
![]() |
f1fbea30f1 | ||
![]() |
04f5bdb843 | ||
![]() |
d29283ff21 | ||
![]() |
35779dad8b | ||
![]() |
ee0d6420a0 | ||
![]() |
f66d847032 | ||
![]() |
aba4dc7c50 | ||
![]() |
99d0c5e2c6 | ||
![]() |
fa55b94528 | ||
![]() |
7fb46cf982 | ||
![]() |
523752b263 | ||
![]() |
7df4b8e8cc | ||
![]() |
7a4ed5a337 | ||
![]() |
ec37085e39 | ||
![]() |
61e1e2729d | ||
![]() |
ae8281f835 | ||
![]() |
2efa68dd60 | ||
![]() |
d2c92781c8 | ||
![]() |
fa35293618 | ||
![]() |
20ccca0aec | ||
![]() |
10e216da6b | ||
![]() |
6491353a57 | ||
![]() |
1906523489 | ||
![]() |
24beaa0d18 | ||
![]() |
e1deb48121 | ||
![]() |
b3891bacaa | ||
![]() |
7939904ee3 | ||
![]() |
7dcd69a2df | ||
![]() |
9f44f989e5 | ||
![]() |
002b2d0134 | ||
![]() |
8b10ec93c2 | ||
![]() |
cf9ecd1564 | ||
![]() |
f31147248b | ||
![]() |
2f9c540660 | ||
![]() |
09489feff9 | ||
![]() |
0d17a69ee4 | ||
![]() |
c5755a4ec4 | ||
![]() |
20bc3719a4 | ||
![]() |
7dc41ab205 | ||
![]() |
acc21e3b43 | ||
![]() |
41a6726beb | ||
![]() |
2f8ba75970 | ||
![]() |
eb08129226 | ||
![]() |
816823b115 | ||
![]() |
b6059939b4 | ||
![]() |
b8e23b2d7e | ||
![]() |
a06846fa74 | ||
![]() |
33f72e40da | ||
![]() |
e8b2d80037 | ||
![]() |
853463b7cd | ||
![]() |
518e1a6405 | ||
![]() |
afd376ddbc | ||
![]() |
1ce325ee8a | ||
![]() |
1e947a34a8 | ||
![]() |
fe1e5aeebf | ||
![]() |
8386b9a0b4 | ||
![]() |
40323e3afe | ||
![]() |
18868a5bd6 | ||
![]() |
754682577c | ||
![]() |
603dee7661 | ||
![]() |
2a27cc46e2 | ||
![]() |
a9a06a1e4e | ||
![]() |
5f73725b8f | ||
![]() |
324983024a | ||
![]() |
7cb63b1da0 | ||
![]() |
31603380c1 | ||
![]() |
5a3d3b0324 | ||
![]() |
78006855ee | ||
![]() |
71520f6709 | ||
![]() |
3f5a09229d | ||
![]() |
5609771993 | ||
![]() |
79b01cdc3c | ||
![]() |
73f99b5e22 | ||
![]() |
7d6f8eb495 | ||
![]() |
eac5340b03 | ||
![]() |
aef0243b73 | ||
![]() |
736053e24e | ||
![]() |
36b196dd93 | ||
![]() |
549dcd32ca | ||
![]() |
a59f56d852 | ||
![]() |
2c14181051 | ||
![]() |
c2b9e06a4a | ||
![]() |
964978d45b | ||
![]() |
599490bb6e | ||
![]() |
a841e1fb2e | ||
![]() |
296fe4b62e | ||
![]() |
cb4c736fab | ||
![]() |
118f02fd11 | ||
![]() |
990d0f6e3e | ||
![]() |
84624666ce | ||
![]() |
42474e551f | ||
![]() |
061e055d1b | ||
![]() |
12f2caa8d6 | ||
![]() |
8bd716c056 | ||
![]() |
b890b5f0dc | ||
![]() |
cd95abb2a1 | ||
![]() |
c3df9e6270 | ||
![]() |
6463fbee32 | ||
![]() |
e43cdc6674 | ||
![]() |
59216a9ae1 | ||
![]() |
47a620bd36 | ||
![]() |
37dbf4d8ec | ||
![]() |
1270f2d577 | ||
![]() |
80a067806d | ||
![]() |
f20065f546 | ||
![]() |
43d50e270a | ||
![]() |
c27117e99e | ||
![]() |
9c84f13425 | ||
![]() |
b2cf7a16f2 | ||
![]() |
28556790d6 | ||
![]() |
41c9bb63a0 | ||
![]() |
c2b2fafc9c | ||
![]() |
5b88ba6d1d | ||
![]() |
d036021a78 | ||
![]() |
7d5e2466f0 | ||
![]() |
d3f35955d6 | ||
![]() |
fb338c0728 | ||
![]() |
5462d1e9f8 | ||
![]() |
2ce8f1ee5d | ||
![]() |
3f0258e215 | ||
![]() |
e72a8d999f | ||
![]() |
fed16fd14e | ||
![]() |
5dbc45ecb9 | ||
![]() |
2f411dfc9c | ||
![]() |
d6cff870e5 | ||
![]() |
e1cd45c57e | ||
![]() |
7572b6f66b | ||
![]() |
e489b08bc1 | ||
![]() |
094bdb29b6 | ||
![]() |
712e05479b | ||
![]() |
7c6c9eed36 | ||
![]() |
63eee28a82 | ||
![]() |
0777eaad10 | ||
![]() |
9e6866c160 | ||
![]() |
974dd8ce57 | ||
![]() |
957d08f4c6 | ||
![]() |
156b499f93 | ||
![]() |
f31100326b | ||
![]() |
9b7b1d6a61 | ||
![]() |
7101ad81c4 | ||
![]() |
8643263227 | ||
![]() |
409c1b7584 | ||
![]() |
eab132ed32 | ||
![]() |
f45b5da71a | ||
![]() |
f57b606f72 | ||
![]() |
66bad2b6f8 | ||
![]() |
44f9bc5f0e | ||
![]() |
5c7e3c6bab | ||
![]() |
46ec504743 | ||
![]() |
737151ba09 | ||
![]() |
2eef39d64a | ||
![]() |
821f76bcd6 | ||
![]() |
37ec058572 | ||
![]() |
cadda12371 | ||
![]() |
a643b56555 | ||
![]() |
aa34df7a4c | ||
![]() |
f7404085de | ||
![]() |
33036e7599 | ||
![]() |
f6e5b67f0d | ||
![]() |
9547ac353d | ||
![]() |
48339b19d4 | ||
![]() |
0f8d464706 | ||
![]() |
d6be7b4cae | ||
![]() |
70aa01b261 | ||
![]() |
11c7ffad4e | ||
![]() |
1973424e05 | ||
![]() |
16d97d3c63 | ||
![]() |
3e6728fedb | ||
![]() |
3e9aea072d | ||
![]() |
fcfb73c755 | ||
![]() |
12ce3282ff | ||
![]() |
9f3e66fff0 | ||
![]() |
624993fc89 | ||
![]() |
ba8a00764a | ||
![]() |
49f79a331c | ||
![]() |
3dec4a6651 | ||
![]() |
ac0a853030 | ||
![]() |
e8ae4e76a3 | ||
![]() |
c73b098f30 | ||
![]() |
4292e26135 | ||
![]() |
02fb2550d0 | ||
![]() |
37bd525638 | ||
![]() |
ea0f37f5b9 | ||
![]() |
d09d7521d6 | ||
![]() |
20d03cb817 | ||
![]() |
97b3c3db7b | ||
![]() |
b97b6dc144 | ||
![]() |
b07bbab069 | ||
![]() |
c8d5218c65 | ||
![]() |
80a657965e | ||
![]() |
b3324d22f5 | ||
![]() |
295663f6a6 | ||
![]() |
47caa2c3a0 | ||
![]() |
4ddc422cfa | ||
![]() |
856c8aa611 | ||
![]() |
6f600f74af | ||
![]() |
be8db602b8 | ||
![]() |
7871e868a9 | ||
![]() |
44467971f2 | ||
![]() |
108fc4a0d8 | ||
![]() |
31b7cdff9b | ||
![]() |
0465298507 | ||
![]() |
65ac8d4b2b | ||
![]() |
dd59e3a831 | ||
![]() |
1e98d56bb6 | ||
![]() |
d31e4c7815 | ||
![]() |
4af1f62aab | ||
![]() |
bc403440ba | ||
![]() |
38d8dfe5ab | ||
![]() |
bee93f4743 | ||
![]() |
9888510158 | ||
![]() |
5fd4ed91d8 | ||
![]() |
2a949cd8f1 | ||
![]() |
312cbc86e9 | ||
![]() |
85ded6e500 | ||
![]() |
ce5a81d83c | ||
![]() |
eb92c0bbf5 | ||
![]() |
5453ae81a6 | ||
![]() |
ff84f2b2f7 | ||
![]() |
6df64d0d31 | ||
![]() |
e9ac20a149 | ||
![]() |
83753a5f81 | ||
![]() |
7a228cac43 | ||
![]() |
3161f5fa47 | ||
![]() |
442741d246 | ||
![]() |
5784092c1b | ||
![]() |
d6ad089c60 | ||
![]() |
89ff8b3fcb | ||
![]() |
5773f141b8 | ||
![]() |
446b4b084c | ||
![]() |
f8da8f6929 | ||
![]() |
f7de055f67 | ||
![]() |
07d09aea85 | ||
![]() |
f63ceed1ae | ||
![]() |
b93d72296c | ||
![]() |
d590e01a58 | ||
![]() |
1bc15a8507 | ||
![]() |
adeb9bccb1 | ||
![]() |
5cf26af9f0 | ||
![]() |
04c9646e05 | ||
![]() |
67bf1a93e4 | ||
![]() |
c263f11170 | ||
![]() |
bb4c646714 | ||
![]() |
b44ffffed8 | ||
![]() |
bd521f858e | ||
![]() |
f66fcfbe6d | ||
![]() |
2bdaf53ecf | ||
![]() |
46e7db6d94 | ||
![]() |
10fc9fe268 | ||
![]() |
73c75635b1 | ||
![]() |
cc7d745ce6 | ||
![]() |
7e1920dc4b | ||
![]() |
a93f05c047 | ||
![]() |
00238247cd | ||
![]() |
b33e28835d | ||
![]() |
044d830b64 | ||
![]() |
38c84bb1f6 | ||
![]() |
b50e066dee | ||
![]() |
f55f803531 | ||
![]() |
757e8eb57c | ||
![]() |
a696afaeb8 | ||
![]() |
8ca298b299 | ||
![]() |
cde497c94e | ||
![]() |
090e29effd | ||
![]() |
94cf6424f5 | ||
![]() |
b8013a57e2 | ||
![]() |
1b23210902 | ||
![]() |
0acca2e313 | ||
![]() |
4bb30deca6 | ||
![]() |
31bf615fe8 | ||
![]() |
0d77027f60 | ||
![]() |
bbf46358fa | ||
![]() |
1cf793233f | ||
![]() |
39b7b3ad53 | ||
![]() |
7acc537c7a | ||
![]() |
97c1a2245b | ||
![]() |
8e9269fdf9 | ||
![]() |
d9b2037b50 | ||
![]() |
95827c3ada | ||
![]() |
4e2bbc04fa | ||
![]() |
61eff6e7e8 | ||
![]() |
1d4d5f0c93 | ||
![]() |
00f1b483eb | ||
![]() |
939de6b177 | ||
![]() |
c3d48acb4c | ||
![]() |
ad4bc206ab | ||
![]() |
8cfa5ba39e | ||
![]() |
c2e6d1c6bf | ||
![]() |
392bda7d8c | ||
![]() |
5a658b7080 | ||
![]() |
a6adb314ec | ||
![]() |
5714578783 | ||
![]() |
539125ff47 | ||
![]() |
10cfcdab8c | ||
![]() |
3f71d3b250 | ||
![]() |
1b50fbab22 | ||
![]() |
303fc65a6a | ||
![]() |
445b6ee13f | ||
![]() |
8afaac1e30 | ||
![]() |
0327f9428e | ||
![]() |
a5de66bbd5 | ||
![]() |
d47157eec3 | ||
![]() |
f4b47ed399 | ||
![]() |
8b2145bd88 | ||
![]() |
de454e8b78 | ||
![]() |
6cd770b4c7 | ||
![]() |
355525c248 | ||
![]() |
53237c297f | ||
![]() |
47d4e7381f | ||
![]() |
5dac6690d7 | ||
![]() |
2d8885cb0c | ||
![]() |
b89f7180db | ||
![]() |
4fdf85bbdb | ||
![]() |
2ebb837a15 | ||
![]() |
728d57d955 | ||
![]() |
e53a2e7b43 | ||
![]() |
e682fd07cb | ||
![]() |
849aa64678 | ||
![]() |
bddd22cfab | ||
![]() |
cbb12e1b7c | ||
![]() |
cc87ba4962 | ||
![]() |
19310470b6 | ||
![]() |
02fcccc8c7 | ||
![]() |
fb2e556726 | ||
![]() |
9f230ae339 | ||
![]() |
3f0eb0a046 | ||
![]() |
7d6d9eddc4 | ||
![]() |
cf87da0ef3 | ||
![]() |
0775acedc0 | ||
![]() |
7b21c1bcbe | ||
![]() |
9c295d1884 | ||
![]() |
8f1cee2e61 | ||
![]() |
caa9cc32d7 | ||
![]() |
b750f827c5 | ||
![]() |
aef53a8753 | ||
![]() |
5d147163e5 | ||
![]() |
75fe1a19eb | ||
![]() |
5149078f6a | ||
![]() |
4d95248f16 | ||
![]() |
5c9405fffc | ||
![]() |
368f52ade6 | ||
![]() |
6457314794 | ||
![]() |
84f4e3eedc | ||
![]() |
8cc2ba4770 | ||
![]() |
b003ed3f03 | ||
![]() |
330da137db | ||
![]() |
9e5d45d0de | ||
![]() |
f40398bf42 | ||
![]() |
b5c15d97fa | ||
![]() |
783a21d88d | ||
![]() |
6ddcba8917 | ||
![]() |
91598cbbbf | ||
![]() |
772c80aa85 | ||
![]() |
62e7c861bd | ||
![]() |
9d5b6eac55 | ||
![]() |
72c5de6eae | ||
![]() |
a28345d858 | ||
![]() |
05b532b9eb | ||
![]() |
0b0d18f182 | ||
![]() |
1d833419aa | ||
![]() |
c1b0877956 | ||
![]() |
46b66c76ef | ||
![]() |
d00b4335b5 | ||
![]() |
7a129e6de1 | ||
![]() |
e17e2a636b | ||
![]() |
17c20276a9 | ||
![]() |
dc9dedf220 | ||
![]() |
3ac772badc | ||
![]() |
22fc58d93b | ||
![]() |
ccd3152b24 | ||
![]() |
7d929dcde6 | ||
![]() |
45e0cbdb25 | ||
![]() |
dc9d48850f | ||
![]() |
3a874bc8c7 | ||
![]() |
8453cd82e9 | ||
![]() |
9092549f07 | ||
![]() |
a94b5ba0c0 | ||
![]() |
5d1efd84a4 | ||
![]() |
f62e56b7ec | ||
![]() |
2ac90bbb96 | ||
![]() |
f85f2d5d22 | ||
![]() |
8af953e20d | ||
![]() |
66132a912a | ||
![]() |
a94269ceb9 | ||
![]() |
b852cbdc80 | ||
![]() |
350caee808 | ||
![]() |
476ac263fb | ||
![]() |
51a4f61a8f | ||
![]() |
7d05236514 | ||
![]() |
465b43be6a | ||
![]() |
006edacd55 | ||
![]() |
a9666a9f6e | ||
![]() |
7de492caa7 | ||
![]() |
93ee4716cc | ||
![]() |
267f5159a3 | ||
![]() |
8a60d4cf2f | ||
![]() |
6489444158 | ||
![]() |
59a66a3ea5 | ||
![]() |
e17550e23e | ||
![]() |
0f8d6daf99 | ||
![]() |
08925a72c6 | ||
![]() |
1101299168 | ||
![]() |
22be8e2e9d | ||
![]() |
6db2240f8a | ||
![]() |
96422de031 | ||
![]() |
54eac18eea | ||
![]() |
f2043dc181 | ||
![]() |
e416ec9279 | ||
![]() |
5eb4ffb1cc | ||
![]() |
284e748449 | ||
![]() |
3111718eb1 | ||
![]() |
fd8d17c5e5 | ||
![]() |
109bb62209 | ||
![]() |
cc661b26fa | ||
![]() |
ea69957ed1 | ||
![]() |
700f641e29 | ||
![]() |
83f4eeb3b5 | ||
![]() |
d1f4cdebf3 | ||
![]() |
d95ba43fd1 | ||
![]() |
8fae964ee8 | ||
![]() |
baf49b88f4 | ||
![]() |
7dc07f6d21 | ||
![]() |
6472d35d91 | ||
![]() |
95c87919a8 | ||
![]() |
077b4d5c89 | ||
![]() |
3577da05ac | ||
![]() |
b8e8028eb9 | ||
![]() |
a2105de402 | ||
![]() |
d049e0a149 | ||
![]() |
7463be862f | ||
![]() |
a899666e68 | ||
![]() |
10a52f8cf9 | ||
![]() |
bc6652f443 | ||
![]() |
72d04a0120 | ||
![]() |
6dbed30008 | ||
![]() |
c5eac298e6 | ||
![]() |
bc18eda336 | ||
![]() |
3cefb14297 | ||
![]() |
4bc401278e | ||
![]() |
56167f84ad | ||
![]() |
d7e3765efe | ||
![]() |
3d51d1e345 | ||
![]() |
db4ecce20b | ||
![]() |
95518f1948 | ||
![]() |
bd23942893 | ||
![]() |
c8610b8ad2 | ||
![]() |
d0440122b9 | ||
![]() |
d4beb2f79b | ||
![]() |
9be995bb08 | ||
![]() |
8d4636bbab | ||
![]() |
f59c6e7a7c | ||
![]() |
c24ab1b21d | ||
![]() |
6a01658355 | ||
![]() |
f1e2439e66 | ||
![]() |
c1d47290b1 | ||
![]() |
4d6116ed40 | ||
![]() |
4d89ed701d | ||
![]() |
18ba394901 | ||
![]() |
3a83753611 | ||
![]() |
e80594d61d | ||
![]() |
c436b586d2 | ||
![]() |
83c6f72eb0 | ||
![]() |
e26299b998 | ||
![]() |
f84e2c2ac7 | ||
![]() |
4e8c94fd2d | ||
![]() |
8f3fd37d47 | ||
![]() |
a839809eb8 | ||
![]() |
7960e9b309 | ||
![]() |
88ceba59cf | ||
![]() |
f368bbec32 | ||
![]() |
021c4ba68a | ||
![]() |
4875544888 | ||
![]() |
849bdc52f6 | ||
![]() |
54f4658dae | ||
![]() |
63bb05b2d4 | ||
![]() |
916ad0a58e | ||
![]() |
dbc67e077d | ||
![]() |
15055fa509 | ||
![]() |
8ee704e123 | ||
![]() |
0d552cd880 | ||
![]() |
e968917dbc | ||
![]() |
d8240bb683 | ||
![]() |
b481c13829 | ||
![]() |
60ce5c67de | ||
![]() |
421f932053 | ||
![]() |
542d6361f2 | ||
![]() |
81bafa00ac | ||
![]() |
84d0c17c4b | ||
![]() |
77c0ba990d | ||
![]() |
be7e2bed6f | ||
![]() |
1d4487b6cd | ||
![]() |
a7bfd0af41 | ||
![]() |
55ef547a85 | ||
![]() |
19129c8786 | ||
![]() |
b5737ce9c1 | ||
![]() |
f921d9a1f8 | ||
![]() |
9609b48f2f | ||
![]() |
ff8145b745 | ||
![]() |
530e8b39e5 | ||
![]() |
5926d396f9 | ||
![]() |
50aeee288b | ||
![]() |
72e001b0d5 | ||
![]() |
f04c9d101e | ||
![]() |
7cf16766c4 | ||
![]() |
0e200e0c34 | ||
![]() |
771a544d0f | ||
![]() |
fba707f6a8 | ||
![]() |
4e28e2cb59 | ||
![]() |
2ecc53ba56 | ||
![]() |
5c3c8b7b8a | ||
![]() |
3eb1fe0eb2 | ||
![]() |
aec998acc1 | ||
![]() |
6a52a1dc63 | ||
![]() |
91e758f66f | ||
![]() |
441416b241 | ||
![]() |
e541d8697e | ||
![]() |
445825df44 | ||
![]() |
b93c47fa60 | ||
![]() |
4b817208aa | ||
![]() |
90b831600f | ||
![]() |
b3dd368920 | ||
![]() |
c8db90b644 | ||
![]() |
7ed65529df | ||
![]() |
a8b59f5f59 | ||
![]() |
eb966ec041 | ||
![]() |
7fea0c3244 | ||
![]() |
588789cb77 | ||
![]() |
3640f977c8 | ||
![]() |
28f12a4874 | ||
![]() |
93378406c3 | ||
![]() |
602d04af82 | ||
![]() |
dcfbf2b154 | ||
![]() |
bd13336256 | ||
![]() |
815940913b | ||
![]() |
f7191c0381 | ||
![]() |
5c6d755750 | ||
![]() |
3c545d488d | ||
![]() |
bd0f84605f | ||
![]() |
07d11c845c | ||
![]() |
6e9a69be5c | ||
![]() |
2e9bd477d9 | ||
![]() |
392df6ba72 | ||
![]() |
c66cffd6a6 | ||
![]() |
b3b3e3ea20 | ||
![]() |
b058fb8db4 | ||
![]() |
9f0f6181a1 | ||
![]() |
067a88b3e7 | ||
![]() |
251d5f4135 | ||
![]() |
f702e1a80d | ||
![]() |
44e574f440 | ||
![]() |
e1527fcbb9 | ||
![]() |
9e583f9ff0 | ||
![]() |
9ba7e5d567 | ||
![]() |
02b6d53544 | ||
![]() |
611816c8e3 | ||
![]() |
0511a62ca1 | ||
![]() |
31e34d6f19 | ||
![]() |
5360fe49d4 | ||
![]() |
bf94febb11 | ||
![]() |
dbd3bd50df | ||
![]() |
c1a1fb8d87 | ||
![]() |
b10568e917 | ||
![]() |
0e1ad39ede | ||
![]() |
123bd0bb92 | ||
![]() |
c8ce06d110 | ||
![]() |
b0cfcb1999 | ||
![]() |
6a8ed1192f | ||
![]() |
0862859f93 | ||
![]() |
3ad336a1eb | ||
![]() |
a17f83cedd | ||
![]() |
2c6850f6e4 | ||
![]() |
5da47636cf | ||
![]() |
944b857825 | ||
![]() |
e04b965659 | ||
![]() |
17d2fb80f2 | ||
![]() |
34c8f33c3c | ||
![]() |
07d74ee692 | ||
![]() |
d28158bc74 | ||
![]() |
14b7ec2a80 | ||
![]() |
8c9fb956ff | ||
![]() |
db632ae847 | ||
![]() |
f27b31b581 | ||
![]() |
8c44147a45 | ||
![]() |
3bef4284e3 | ||
![]() |
ec05215a5e | ||
![]() |
5229b2959e | ||
![]() |
8769234e28 | ||
![]() |
bbdd1915eb | ||
![]() |
5903e8256f | ||
![]() |
c879351063 | ||
![]() |
de8a244500 | ||
![]() |
1bb7e36a65 | ||
![]() |
3066a142b8 | ||
![]() |
793a01f7ca | ||
![]() |
ccf047b1ab | ||
![]() |
40c8fdbf64 | ||
![]() |
dc01c907f1 | ||
![]() |
801df94446 | ||
![]() |
0197d89976 | ||
![]() |
ce725252cc | ||
![]() |
e16a67242e | ||
![]() |
4c678a5010 | ||
![]() |
73f07b2939 | ||
![]() |
b58efa3df0 | ||
![]() |
9f885407f5 | ||
![]() |
3754088a44 | ||
![]() |
c4f084a991 | ||
![]() |
4c73df4ba6 | ||
![]() |
4aa53aa5a5 | ||
![]() |
7483d3b229 | ||
![]() |
8b6cc708e7 | ||
![]() |
200960899e | ||
![]() |
599a456c81 | ||
![]() |
4b46502d22 | ||
![]() |
7233c55428 | ||
![]() |
a58c5cce78 | ||
![]() |
0b23bf65b3 | ||
![]() |
bc0a3f8a47 | ||
![]() |
ef59fd4b6f | ||
![]() |
9b2a0102be | ||
![]() |
04b4ef6d85 | ||
![]() |
9e8aadb750 | ||
![]() |
36e7c2467e | ||
![]() |
f7ce83ea34 | ||
![]() |
0ae0f40628 | ||
![]() |
81182bb125 | ||
![]() |
2424df0d18 | ||
![]() |
af9aa7d201 | ||
![]() |
4cd3a614de | ||
![]() |
1e5420e6a7 | ||
![]() |
660de0b4e5 | ||
![]() |
b73aaecd22 | ||
![]() |
e9d07eadaa | ||
![]() |
6f0e944c7e | ||
![]() |
cd8d2c141e | ||
![]() |
c831d62bc3 | ||
![]() |
14ac66ff4e | ||
![]() |
75bf758042 | ||
![]() |
1539e703e9 | ||
![]() |
f43bf03768 | ||
![]() |
495f7f190f | ||
![]() |
3acc521741 | ||
![]() |
16216b9eb9 | ||
![]() |
dfdb22f584 | ||
![]() |
b7fb9e182b | ||
![]() |
8574bf9d98 | ||
![]() |
1a80439825 | ||
![]() |
0b264176bc | ||
![]() |
bde70a27f0 | ||
![]() |
7d2f5f0799 | ||
![]() |
28bc07da2f | ||
![]() |
7610ab7a8d | ||
![]() |
8517cc8211 | ||
![]() |
51db653b1a | ||
![]() |
8aa8ae239a | ||
![]() |
dc4e4395a9 | ||
![]() |
623694ab73 | ||
![]() |
374457df70 | ||
![]() |
7885dddef2 | ||
![]() |
024ec86dc5 | ||
![]() |
73d6cc1e54 | ||
![]() |
f632ef0de8 | ||
![]() |
8fdf84068d | ||
![]() |
c58ad64a28 | ||
![]() |
131625bb53 | ||
![]() |
cbf3ae4db4 | ||
![]() |
9ac7acf8b3 | ||
![]() |
9dadb6da4c | ||
![]() |
b6bbbeb9d3 | ||
![]() |
517e9f92ba | ||
![]() |
29c9e5cb17 | ||
![]() |
52b60fd6a6 | ||
![]() |
7ec09c80a5 | ||
![]() |
b0bfe341df | ||
![]() |
d6337f7500 | ||
![]() |
70cf6546ca | ||
![]() |
0cdab52418 | ||
![]() |
625e04d208 | ||
![]() |
6da657d3e2 | ||
![]() |
777a95d23c | ||
![]() |
06caace827 | ||
![]() |
0a0a766c0d | ||
![]() |
344c9e9238 | ||
![]() |
89b2b066ef | ||
![]() |
894e084c7f | ||
![]() |
2ba064b2a5 | ||
![]() |
dfe065ef82 | ||
![]() |
7bd4b78470 | ||
![]() |
d7991a247d | ||
![]() |
2f9eacdf66 | ||
![]() |
2178fd6ee9 | ||
![]() |
7019ddb165 | ||
![]() |
9f13763637 | ||
![]() |
beeba27f46 | ||
![]() |
79ac85e048 | ||
![]() |
315d4f225a | ||
![]() |
85489458d8 | ||
![]() |
bfc7f56c4d | ||
![]() |
23c5ddce83 | ||
![]() |
7685f9b73d | ||
![]() |
3bb9d220bb | ||
![]() |
664fad96fa | ||
![]() |
669a610e36 | ||
![]() |
7e0d9cb48c | ||
![]() |
7cbc9d21b5 | ||
![]() |
62cb8358cc | ||
![]() |
55b26751ae | ||
![]() |
c2892d7887 | ||
![]() |
6c8bf090fe | ||
![]() |
ada5fd53e7 | ||
![]() |
2f30451067 | ||
![]() |
6b5c2be701 | ||
![]() |
13bc378069 | ||
![]() |
1ba70706c2 | ||
![]() |
8431d0bd5c | ||
![]() |
852f758be3 | ||
![]() |
b455f432d5 | ||
![]() |
6760744249 | ||
![]() |
306cea60a1 | ||
![]() |
ba2e07c4b9 | ||
![]() |
9b796531b2 | ||
![]() |
11c7d586d9 | ||
![]() |
39cd83b171 | ||
![]() |
2fdbc88d8c | ||
![]() |
cc0f1be5d2 | ||
![]() |
08d7a1c123 | ||
![]() |
1f70a735c7 | ||
![]() |
6713fcfeb1 | ||
![]() |
c3107d213a | ||
![]() |
adf5c8c278 | ||
![]() |
5f86a8a15b | ||
![]() |
042c756be8 | ||
![]() |
1952505e52 | ||
![]() |
c4086b9127 | ||
![]() |
0ada09891c | ||
![]() |
2d586406da | ||
![]() |
651b4d2461 | ||
![]() |
01b2468fea | ||
![]() |
371c4e0051 | ||
![]() |
69099fcdd7 | ||
![]() |
28e714db1e | ||
![]() |
57e50d0c33 | ||
![]() |
a0fd02e0c0 | ||
![]() |
c925b3d218 | ||
![]() |
5da4386f31 | ||
![]() |
4104dec87f | ||
![]() |
d40a555531 | ||
![]() |
95df91a03b | ||
![]() |
3a8caa15b9 | ||
![]() |
a5a25f02e3 | ||
![]() |
bfd7be543a | ||
![]() |
778e790e82 | ||
![]() |
17eeb22971 | ||
![]() |
1617658bfe | ||
![]() |
991fe31569 | ||
![]() |
4bcfff780a | ||
![]() |
12f9ad8f7f | ||
![]() |
6f843fcb27 | ||
![]() |
e0f17e1778 | ||
![]() |
4a5a9b73b1 | ||
![]() |
7652336c84 | ||
![]() |
94113827a7 | ||
![]() |
cc10ee0134 | ||
![]() |
5ad0fdf39c | ||
![]() |
7201a8d634 | ||
![]() |
3fde7365f9 | ||
![]() |
f6a5bc9b40 | ||
![]() |
9a5917a331 | ||
![]() |
94c0324e8a | ||
![]() |
d3b5594092 | ||
![]() |
742d580eae | ||
![]() |
d4a3cadd09 | ||
![]() |
ec0feb68f4 | ||
![]() |
bfb27c49a2 | ||
![]() |
cb7b7f1dca | ||
![]() |
38c032b79e | ||
![]() |
2b7f2d4744 | ||
![]() |
5ca8bc3f2a | ||
![]() |
1ccc8eec0a | ||
![]() |
9c5afda83a | ||
![]() |
05b86a71fd | ||
![]() |
d94d3d4bc5 | ||
![]() |
e0c0f29fc6 | ||
![]() |
119826cb9b | ||
![]() |
6ab95ed4ef | ||
![]() |
4f1eb64ac6 | ||
![]() |
3e3dc3a6ab | ||
![]() |
3f8dc76f84 | ||
![]() |
8a6945ff3b | ||
![]() |
f60579fd21 | ||
![]() |
e6111c0d48 | ||
![]() |
136a00a301 | ||
![]() |
b2f5bee20d | ||
![]() |
fa075f6800 | ||
![]() |
277f395595 | ||
![]() |
e2061464a5 | ||
![]() |
04c4451f7d | ||
![]() |
fcf5cd4655 | ||
![]() |
3816f0b68b | ||
![]() |
ccab1844ce | ||
![]() |
5a6be54970 | ||
![]() |
ce6b5105c6 | ||
![]() |
de7da8b26e | ||
![]() |
24932d6ba3 | ||
![]() |
d5fd5954d1 | ||
![]() |
977075763d | ||
![]() |
757172934e | ||
![]() |
1a2543ddde | ||
![]() |
1a520f8782 | ||
![]() |
b56c1b956c | ||
![]() |
1a279d14c4 | ||
![]() |
0d8e763a5f | ||
![]() |
bf6d3649a4 | ||
![]() |
7c9db7edeb | ||
![]() |
65fd705d9a | ||
![]() |
311e54451b | ||
![]() |
8b759bc5d9 | ||
![]() |
9a0aac4745 | ||
![]() |
cb6607a169 | ||
![]() |
8caa4e9cb6 | ||
![]() |
d6338d7b11 | ||
![]() |
f96a5ec774 | ||
![]() |
1be8e7e216 | ||
![]() |
cf9cf9d7bb | ||
![]() |
6ba1795ded | ||
![]() |
ced0cc1bac | ||
![]() |
3c49f22266 | ||
![]() |
13ae99edec | ||
![]() |
0f82730a78 | ||
![]() |
8608c45309 | ||
![]() |
90516217e0 | ||
![]() |
ff083daf31 | ||
![]() |
7f6a554e1b | ||
![]() |
eb99271120 | ||
![]() |
04aa22b510 | ||
![]() |
13e5c695c3 | ||
![]() |
1cd56decab | ||
![]() |
585f8f4683 | ||
![]() |
12de47c923 | ||
![]() |
afde7940d8 | ||
![]() |
1070d8d3fa | ||
![]() |
dcc7ba8f93 | ||
![]() |
01dd41bdbf | ||
![]() |
7d5b20314c | ||
![]() |
29e048af7b | ||
![]() |
f3b84f1365 | ||
![]() |
5751d5c1b0 | ||
![]() |
3d2336aac1 | ||
![]() |
afe5f19464 | ||
![]() |
4091a3c238 | ||
![]() |
c38f0d751b | ||
![]() |
3623afa721 | ||
![]() |
f411e07fb4 | ||
![]() |
9bfe27dd5e | ||
![]() |
6a096fbb27 | ||
![]() |
c4201d9a2a | ||
![]() |
58e9817a6d | ||
![]() |
48d5584491 | ||
![]() |
6404071a01 | ||
![]() |
735205492e | ||
![]() |
5786f1d057 | ||
![]() |
87c6f3c757 | ||
![]() |
a6feb77e52 | ||
![]() |
0e99c948d6 | ||
![]() |
0f5e0f640b | ||
![]() |
3d2c6388de | ||
![]() |
13b3b2fd23 | ||
![]() |
ad8e614ae8 | ||
![]() |
9a6d709082 | ||
![]() |
48c0360877 | ||
![]() |
6eafab8286 | ||
![]() |
d230be3e1c | ||
![]() |
daa77d40a3 | ||
![]() |
c901512db0 | ||
![]() |
6ae743684f | ||
![]() |
789c00dde1 | ||
![]() |
90da471084 | ||
![]() |
2d55056015 | ||
![]() |
ffab9bb893 | ||
![]() |
1192d04391 | ||
![]() |
f18dced2f3 | ||
![]() |
31ea032054 | ||
![]() |
89543e927a | ||
![]() |
a52386e6ad | ||
![]() |
ecdc0a3800 | ||
![]() |
46d66dded8 | ||
![]() |
a69dcfc49d | ||
![]() |
601005f837 | ||
![]() |
de6f32e486 | ||
![]() |
fbe866198d | ||
![]() |
70546cd2ec | ||
![]() |
a7c99cbbd2 | ||
![]() |
40780ccec7 | ||
![]() |
7078c91f7d | ||
![]() |
0e2168392c | ||
![]() |
380006c9d8 | ||
![]() |
4127882e5f | ||
![]() |
d603a8a9be | ||
![]() |
5f17d30973 | ||
![]() |
8f92077454 | ||
![]() |
8755a9bcda | ||
![]() |
6ace46eece | ||
![]() |
37cab07295 | ||
![]() |
4a1ecc7b72 | ||
![]() |
e46078286d | ||
![]() |
cfeb88f649 | ||
![]() |
7ce197e0c8 | ||
![]() |
f93b1167f1 | ||
![]() |
152ca63529 | ||
![]() |
806221bc3f | ||
![]() |
2e8d5311a5 | ||
![]() |
23b5fd1c12 | ||
![]() |
7a8ba7d47d | ||
![]() |
1a509cf3e0 | ||
![]() |
9848f9613c | ||
![]() |
4264c2a173 | ||
![]() |
93cefb88f5 | ||
![]() |
7132e1fee1 | ||
![]() |
e70e1b8ad7 | ||
![]() |
77d8a8e43d | ||
![]() |
0b75a7d0d3 | ||
![]() |
1f4a15ee6b | ||
![]() |
f6772eaf59 | ||
![]() |
bab4f9963f | ||
![]() |
1dbcac4f53 | ||
![]() |
7ac5abe7f8 | ||
![]() |
157e6b2a33 | ||
![]() |
8d20a13776 | ||
![]() |
a385ea7c52 | ||
![]() |
c58a3c41d8 | ||
![]() |
0a7df86f3f | ||
![]() |
213bc75ae1 | ||
![]() |
f8df7ebb7c | ||
![]() |
60503c31fb | ||
![]() |
77220e24dd | ||
![]() |
3c25e11c5f | ||
![]() |
be2ffc31b2 | ||
![]() |
8a2256e0d8 | ||
![]() |
adfb24ce02 | ||
![]() |
e6d50f94ee | ||
![]() |
520798bfa6 | ||
![]() |
e539a36ae7 | ||
![]() |
bfab2d405b | ||
![]() |
df38f00cf2 | ||
![]() |
a30ce1c44d | ||
![]() |
eee2450c9e | ||
![]() |
baf4a241a2 | ||
![]() |
e2242f5d99 | ||
![]() |
30df67721d | ||
![]() |
4a20f43fbf | ||
![]() |
c59e792178 | ||
![]() |
e6a99c1d33 | ||
![]() |
3548628c2c | ||
![]() |
bb84157a21 | ||
![]() |
eb10aa8c97 | ||
![]() |
e06d269b82 | ||
![]() |
07428922c3 | ||
![]() |
0f7e22d8b7 | ||
![]() |
ba4c3e3852 | ||
![]() |
b8de36b340 | ||
![]() |
bbe3e8093c | ||
![]() |
26096bc136 | ||
![]() |
caae57d960 | ||
![]() |
f91384596c | ||
![]() |
cb38976162 | ||
![]() |
3460f9d9cc | ||
![]() |
6eacf8ed7e | ||
![]() |
20bebe98b1 | ||
![]() |
91d885eae4 | ||
![]() |
ea3358ecb2 | ||
![]() |
4c5c4d1700 | ||
![]() |
e3fabe92bd | ||
![]() |
142740f080 | ||
![]() |
09e51c2399 | ||
![]() |
0892eb271d | ||
![]() |
536be76ecb | ||
![]() |
6bfdf0eb4d | ||
![]() |
8320ed5a92 | ||
![]() |
43677685bb | ||
![]() |
61e0aa9845 | ||
![]() |
34eee005a8 | ||
![]() |
6fa136da0c | ||
![]() |
9b3e6270d5 | ||
![]() |
b5abc6c724 | ||
![]() |
71edc3a084 | ||
![]() |
f74a45a33e | ||
![]() |
0a1d04495d | ||
![]() |
49dee560fd | ||
![]() |
dfb3dfb74d | ||
![]() |
05521bfd3a | ||
![]() |
6c997f573a | ||
![]() |
c4689c3bcc | ||
![]() |
c0dd98b6d7 | ||
![]() |
83452d73bc | ||
![]() |
e5417d12ca | ||
![]() |
6e19e6f0a0 | ||
![]() |
071281c13a | ||
![]() |
56cf1c818b | ||
![]() |
61101987f9 | ||
![]() |
a057e50684 | ||
![]() |
5f3dc660c8 | ||
![]() |
5553964d52 | ||
![]() |
51ead2f6bd | ||
![]() |
d433b25627 | ||
![]() |
bd7671c07e | ||
![]() |
847178b7be | ||
![]() |
90808ac67e | ||
![]() |
a899ea8b4d | ||
![]() |
7b83b99ac9 | ||
![]() |
7aab7678e9 | ||
![]() |
195af002cf | ||
![]() |
38db8d2bfd | ||
![]() |
44a9fed8a9 | ||
![]() |
cfc346abad | ||
![]() |
befeb55349 | ||
![]() |
b460d0f533 | ||
![]() |
c11acb6308 | ||
![]() |
d76103eb76 | ||
![]() |
779f984a30 | ||
![]() |
9c55017191 | ||
![]() |
c6f575d8d3 | ||
![]() |
2c0c22dbf3 | ||
![]() |
73a99a7dea | ||
![]() |
3c81337630 | ||
![]() |
3f41ba6bdf | ||
![]() |
2812f61957 | ||
![]() |
f23cee17eb | ||
![]() |
746a8badac | ||
![]() |
f24fcfca69 | ||
![]() |
d60506a75d | ||
![]() |
77dee439e6 | ||
![]() |
d7790a04c5 | ||
![]() |
2c0b07387b | ||
![]() |
06f2f9adbb | ||
![]() |
37d5b9109f | ||
![]() |
76e269ee21 | ||
![]() |
3103939197 | ||
![]() |
3f01c87223 | ||
![]() |
e4cda4bb99 | ||
![]() |
f333f867c5 | ||
![]() |
75c46f7a0e | ||
![]() |
5c6cb41124 | ||
![]() |
94f7c03871 | ||
![]() |
96d5c03a6d | ||
![]() |
3a03bc41a7 | ||
![]() |
331844ff73 | ||
![]() |
2bc38e3784 | ||
![]() |
d580dedfc8 | ||
![]() |
aa0f4c9985 | ||
![]() |
aa242d897d | ||
![]() |
d56ab6c971 | ||
![]() |
2f7be3475d | ||
![]() |
2544d2e068 | ||
![]() |
a09f64aee5 | ||
![]() |
f7114fc2aa | ||
![]() |
7092f337ef | ||
![]() |
8e71c3ae17 | ||
![]() |
a9c211d66c | ||
![]() |
a1c2c04510 | ||
![]() |
0902b628f8 | ||
![]() |
517a85f9e9 | ||
![]() |
94941a7732 | ||
![]() |
f3aa8d368e | ||
![]() |
9518c5f2e4 | ||
![]() |
e44173ff09 | ||
![]() |
fa1106d3cf | ||
![]() |
c24d574f90 | ||
![]() |
8ed35652bc | ||
![]() |
9bdcfc8a45 | ||
![]() |
89b76b514c | ||
![]() |
dd433d8af0 | ||
![]() |
c105f3b970 | ||
![]() |
94471c0d1c | ||
![]() |
849e04ab83 | ||
![]() |
67833c5513 | ||
![]() |
b0306867b4 | ||
![]() |
d5025fdbcc | ||
![]() |
e6cf1dc98d | ||
![]() |
0266370218 | ||
![]() |
aeb3f2b018 | ||
![]() |
bb8d5ac13f | ||
![]() |
d02bf37167 | ||
![]() |
99dbd9e649 | ||
![]() |
0d63dad8c2 | ||
![]() |
6ce465664f | ||
![]() |
b934634159 | ||
![]() |
ef904e01ec | ||
![]() |
25b77db4cd | ||
![]() |
88c0a9e30a | ||
![]() |
75c219d6c6 | ||
![]() |
82e7328903 | ||
![]() |
d6b366c77f | ||
![]() |
42a7c84a33 | ||
![]() |
1f4c1f2af5 | ||
![]() |
e665e4dc57 | ||
![]() |
9551519a35 | ||
![]() |
df51b80e07 | ||
![]() |
0df6826c91 | ||
![]() |
af61962314 | ||
![]() |
f5ed710c0b | ||
![]() |
e5ae07b3e8 | ||
![]() |
f1535e1f71 | ||
![]() |
a6316b40d1 | ||
![]() |
c2746a55e3 | ||
![]() |
e9c782bf9e | ||
![]() |
ad6c154eb6 | ||
![]() |
b747b10642 | ||
![]() |
2005a2abd4 | ||
![]() |
c5818ff5e4 | ||
![]() |
b5e02e6ff9 | ||
![]() |
205f62e732 | ||
![]() |
fdc8b5eb54 | ||
![]() |
36abe8e808 | ||
![]() |
fcf0e08e01 | ||
![]() |
0edcf97e3f | ||
![]() |
0707b26303 | ||
![]() |
09662a4bcd | ||
![]() |
cb06961b82 | ||
![]() |
824fb4adae | ||
![]() |
a91d993c6d | ||
![]() |
e16bab8dd9 | ||
![]() |
068c5851ef | ||
![]() |
af48dcd884 | ||
![]() |
058806c241 | ||
![]() |
48f6e33bf2 | ||
![]() |
6a3ef42d37 | ||
![]() |
f10a9d7f61 | ||
![]() |
c315c04b9a | ||
![]() |
f7893d4e4b | ||
![]() |
2e2f7fa6c3 | ||
![]() |
7aff64f877 | ||
![]() |
0d05bc2676 | ||
![]() |
131b350ee7 | ||
![]() |
06651dbc4c | ||
![]() |
0296247d82 | ||
![]() |
79e767ba79 | ||
![]() |
f771dee852 | ||
![]() |
c4d8ef5954 | ||
![]() |
5d6b97a63e | ||
![]() |
c277ebb43e | ||
![]() |
c9cd7b087a | ||
![]() |
da7ff52a2f | ||
![]() |
84e4a4bb52 | ||
![]() |
34c9c5a9b1 | ||
![]() |
e348e66f14 | ||
![]() |
1e83614f8b | ||
![]() |
c53647cd18 | ||
![]() |
faa4e7753c | ||
![]() |
220757be06 | ||
![]() |
7f1013123a | ||
![]() |
b1a7fe0f80 | ||
![]() |
153e6b56ba | ||
![]() |
35998df05c | ||
![]() |
5176a8cb9e | ||
![]() |
4892b648ae | ||
![]() |
b4506c2b9b | ||
![]() |
4373163869 | ||
![]() |
4b8cde7b3b | ||
![]() |
b7543c8a45 | ||
![]() |
b5232886d9 | ||
![]() |
aefd81228e | ||
![]() |
03516e11f7 | ||
![]() |
455a17cdb2 | ||
![]() |
564ad09b98 | ||
![]() |
135f6b0050 | ||
![]() |
d5e79ff36c | ||
![]() |
7a7f34746e | ||
![]() |
6b4bbe625b | ||
![]() |
28c0b2737b | ||
![]() |
7684fb83d6 | ||
![]() |
4d22608724 | ||
![]() |
b7a42d61c4 | ||
![]() |
bdaef7e541 | ||
![]() |
854501385e | ||
![]() |
7b875ed378 | ||
![]() |
c930d6ddc0 | ||
![]() |
aab2e4555a | ||
![]() |
938eefae9b | ||
![]() |
eccc5e6d61 | ||
![]() |
bc96ca48b6 | ||
![]() |
e5dd88cdce | ||
![]() |
51acd4952b | ||
![]() |
88e0da765c | ||
![]() |
3ba708b98d | ||
![]() |
dfb6216271 | ||
![]() |
70387e6976 | ||
![]() |
55fd781c77 | ||
![]() |
36a0a240f9 | ||
![]() |
ed0b507fb5 | ||
![]() |
3a3948e74f | ||
![]() |
c01dd232c5 | ||
![]() |
825cc096c0 | ||
![]() |
bcd574036e | ||
![]() |
634fe64dca | ||
![]() |
e33a0bf940 | ||
![]() |
fb91d9b2d4 | ||
![]() |
3d89588eb1 | ||
![]() |
ac010cd7b2 | ||
![]() |
d0c94ba565 | ||
![]() |
adbeb4ae54 | ||
![]() |
c7e1b47c59 | ||
![]() |
b25e22bca4 | ||
![]() |
6cb6a8ee98 | ||
![]() |
223bd969fd | ||
![]() |
ae46eeabef | ||
![]() |
79c83a96a0 | ||
![]() |
db33eb5b1f | ||
![]() |
bc45f8f1d8 | ||
![]() |
7875a96137 | ||
![]() |
4875f5e275 | ||
![]() |
7f9da22626 | ||
![]() |
78098e612d | ||
![]() |
e19ab9a0d5 | ||
![]() |
ace45516d9 | ||
![]() |
fe57bfce1b | ||
![]() |
453cc0cc05 | ||
![]() |
39009c482d | ||
![]() |
6b942be1cd | ||
![]() |
77aa2b6ba9 | ||
![]() |
074d53fa17 | ||
![]() |
a839ec4832 | ||
![]() |
5119799256 | ||
![]() |
176e18e4c0 | ||
![]() |
b697df35c6 | ||
![]() |
5236625485 | ||
![]() |
f36073f5cd | ||
![]() |
595fd9377c | ||
![]() |
76f4c49e22 | ||
![]() |
8c0592cdb7 | ||
![]() |
0c73ecf6a8 | ||
![]() |
c365fd9d74 | ||
![]() |
aa8c587ac8 | ||
![]() |
e9d9760e16 | ||
![]() |
560f72a320 | ||
![]() |
3425f2bf34 | ||
![]() |
e28dbb3b93 | ||
![]() |
1b3ed80d37 | ||
![]() |
89a2e15cfb | ||
![]() |
99b1282e98 | ||
![]() |
ed753b590c | ||
![]() |
bda095b0ec | ||
![]() |
9d7f1d230c | ||
![]() |
bdf0458780 | ||
![]() |
7f973ad131 | ||
![]() |
1f42070104 | ||
![]() |
98e4ac6b27 | ||
![]() |
8d318e7206 | ||
![]() |
362156c8ab | ||
![]() |
3ff23ade8b | ||
![]() |
d9050dd8b9 | ||
![]() |
bcee2c37d7 | ||
![]() |
b8fcab29ac | ||
![]() |
8ddae6bba0 | ||
![]() |
fc9255cdc4 | ||
![]() |
f725506df0 | ||
![]() |
aa27b85538 | ||
![]() |
afe75f8a89 | ||
![]() |
b725d66ee3 | ||
![]() |
fe3fb622ee | ||
![]() |
e8bd114c01 | ||
![]() |
b694bcf62a | ||
![]() |
b0f933c926 | ||
![]() |
2d12bc01de | ||
![]() |
17b4ff7a1f | ||
![]() |
6aff9f91d5 | ||
![]() |
cac8fd93e4 | ||
![]() |
f046abefbc | ||
![]() |
b34e060b17 | ||
![]() |
907be77ef4 | ||
![]() |
373d12be64 | ||
![]() |
bf45d55b4a | ||
![]() |
2525429070 | ||
![]() |
2513bcf2c6 | ||
![]() |
bbffb6d901 | ||
![]() |
3b7f5a1397 | ||
![]() |
600e949628 | ||
![]() |
235c123ad0 | ||
![]() |
dcea584a9a | ||
![]() |
d583adb958 | ||
![]() |
0d96a0fb7c | ||
![]() |
fb6b2cbfa2 | ||
![]() |
eb65eafbe0 | ||
![]() |
13f18492ad | ||
![]() |
3d04541ee3 | ||
![]() |
6ded0cce12 | ||
![]() |
1ecbaf2c7f | ||
![]() |
b0828a6280 | ||
![]() |
d2ffb3ca9d | ||
![]() |
606cd18dc4 | ||
![]() |
8a713b2bbb | ||
![]() |
c68f31a11c | ||
![]() |
3107943af8 | ||
![]() |
bf9360f374 | ||
![]() |
f66c28a1bc | ||
![]() |
5d6e214e0e | ||
![]() |
ee8596d175 | ||
![]() |
3c900814be | ||
![]() |
52df963be9 | ||
![]() |
8c513c2aad | ||
![]() |
1e3834dc3d | ||
![]() |
386b10a004 | ||
![]() |
f697f3d7ad | ||
![]() |
8d3ff16037 | ||
![]() |
b10ab358da | ||
![]() |
2cc6edd868 | ||
![]() |
cac974b8e1 | ||
![]() |
2c02e27197 | ||
![]() |
85c0002a78 | ||
![]() |
bd65bf2175 | ||
![]() |
d70332f2a2 | ||
![]() |
35b54e2f88 | ||
![]() |
a3e1af72ab | ||
![]() |
1313a44ba3 | ||
![]() |
edbe8131ab | ||
![]() |
ff55a305cd | ||
![]() |
10c22efbb5 | ||
![]() |
118bbfbcf7 | ||
![]() |
e7709d8463 | ||
![]() |
d70e421db3 | ||
![]() |
0028c3c640 | ||
![]() |
2594cb23c4 | ||
![]() |
d975f6e23a | ||
![]() |
5e46975c63 | ||
![]() |
16f41fc4b4 | ||
![]() |
1263f5e046 | ||
![]() |
96713ef383 | ||
![]() |
26a8686a54 | ||
![]() |
57421d2392 | ||
![]() |
63d92381ee | ||
![]() |
2716f4cbe9 | ||
![]() |
6ef4582ebd | ||
![]() |
649ebb8a4e | ||
![]() |
b1acf6088f | ||
![]() |
78f5d80c5b | ||
![]() |
5896b1eb71 | ||
![]() |
559891ac43 | ||
![]() |
1fbe1d1855 | ||
![]() |
71473fd5c4 | ||
![]() |
c7beacf3b9 | ||
![]() |
3dc55ff8b5 | ||
![]() |
b6789e0011 | ||
![]() |
2f2bebe506 | ||
![]() |
503b8cbf34 | ||
![]() |
916356afad | ||
![]() |
41a84160ec | ||
![]() |
55f071e4a0 | ||
![]() |
0afe0f8fbd | ||
![]() |
427b5bd126 | ||
![]() |
20f6378a3e | ||
![]() |
36e0a1eb23 | ||
![]() |
ac9a567e1f | ||
![]() |
33cd52cddd | ||
![]() |
d543ba82ac | ||
![]() |
5a98993df9 | ||
![]() |
aee9c4a193 | ||
![]() |
0262f89bb0 | ||
![]() |
a8c530a3a9 | ||
![]() |
7dc1240c18 | ||
![]() |
3b4bccd0a7 | ||
![]() |
39a80f8dc7 | ||
![]() |
ce446dac92 | ||
![]() |
b1c9dbd0a5 | ||
![]() |
a5c57d7fb0 | ||
![]() |
0a349e5298 | ||
![]() |
dc7d7150cd | ||
![]() |
7c4aa4441f | ||
![]() |
62a795bd3b | ||
![]() |
c583ab4f1a | ||
![]() |
45a2970307 | ||
![]() |
56a74bc54c | ||
![]() |
5a4b719995 | ||
![]() |
34a4a89bc9 | ||
![]() |
02989f65ff | ||
![]() |
1da539ef05 | ||
![]() |
38d6fbd3d3 | ||
![]() |
39682a4cf1 | ||
![]() |
993a5805cd | ||
![]() |
09cf528cc6 | ||
![]() |
18ab2b3ab5 | ||
![]() |
655fbf91e2 | ||
![]() |
980794ea43 | ||
![]() |
c1cab30daf | ||
![]() |
b50e798d55 | ||
![]() |
230e3ef842 | ||
![]() |
2635a3edd0 | ||
![]() |
47f24ac151 | ||
![]() |
42397a01bf | ||
![]() |
7730a00dbb | ||
![]() |
3a4f4c8c34 | ||
![]() |
a0d88b0cbe | ||
![]() |
bb0419837b | ||
![]() |
35098c474c | ||
![]() |
0ca6535345 | ||
![]() |
ec6a243e3e | ||
![]() |
2cd8ee4a13 | ||
![]() |
432755e05d | ||
![]() |
51a7c2adc5 | ||
![]() |
5c1f793887 | ||
![]() |
a20358b61b | ||
![]() |
e5dc473ced | ||
![]() |
e57a518b34 | ||
![]() |
341d9d1697 | ||
![]() |
9cee424383 | ||
![]() |
37cb51cfd6 | ||
![]() |
533f86c035 | ||
![]() |
9947c51af7 | ||
![]() |
409303d0f5 | ||
![]() |
84b7bfb989 | ||
![]() |
04d17e4839 | ||
![]() |
10a1275a52 | ||
![]() |
5464cbc2ed | ||
![]() |
3b3f8e6f43 | ||
![]() |
2d4d7bf937 | ||
![]() |
652651668f | ||
![]() |
e57d5d86f3 | ||
![]() |
4f7dc5be34 | ||
![]() |
b2c656940b | ||
![]() |
f8266d9811 | ||
![]() |
5c03550a8a | ||
![]() |
ed5491a480 | ||
![]() |
e8371f403b | ||
![]() |
373cffb261 | ||
![]() |
2bf22766eb | ||
![]() |
83530fcaf1 | ||
![]() |
6a41123209 | ||
![]() |
c895c5c16e | ||
![]() |
9ab4b3f3f4 | ||
![]() |
b93dd47a01 | ||
![]() |
69a826f896 | ||
![]() |
60c7ec5aca | ||
![]() |
888eacb574 | ||
![]() |
4def582ab1 | ||
![]() |
ee6f25cc47 | ||
![]() |
f5d6383f50 | ||
![]() |
00f00b8198 | ||
![]() |
3d1f883465 | ||
![]() |
8613a89931 | ||
![]() |
dd1485a66a | ||
![]() |
4198954d46 | ||
![]() |
e56caa8959 | ||
![]() |
de7c72d5a7 | ||
![]() |
18a4a43f8a | ||
![]() |
70c73d2e0e | ||
![]() |
2049e8b8df | ||
![]() |
41da889a09 | ||
![]() |
0a1bbca321 | ||
![]() |
7e01d113d8 | ||
![]() |
6e49cc54ac | ||
![]() |
487a95eb79 | ||
![]() |
696e438df7 | ||
![]() |
a4e093159e | ||
![]() |
23986579bb | ||
![]() |
0fc099926f | ||
![]() |
06b9b66272 | ||
![]() |
fec8343f45 | ||
![]() |
2d8f2593f0 | ||
![]() |
ab0a03a420 | ||
![]() |
3ef30a42be | ||
![]() |
034687163f | ||
![]() |
190db80c28 | ||
![]() |
579f355852 | ||
![]() |
d9104433a8 | ||
![]() |
c6059f9bf6 | ||
![]() |
fd06ff08c7 | ||
![]() |
afd990bd71 | ||
![]() |
8333c4b48d | ||
![]() |
30c4f501fc | ||
![]() |
3b96eef2ad | ||
![]() |
a769ab6cff | ||
![]() |
ef1f835e57 | ||
![]() |
329b2ba275 | ||
![]() |
98383ef19b | ||
![]() |
8bb70fb1b0 | ||
![]() |
72cca365e5 | ||
![]() |
dfc99faea8 | ||
![]() |
69a2ac8de1 | ||
![]() |
8bfae2fa74 | ||
![]() |
2788dd1ada | ||
![]() |
cb0452964e | ||
![]() |
8048bb85fb |
17
.devcontainer/Dockerfile
Normal file
@ -0,0 +1,17 @@
|
||||
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.148.1/containers/python-3/.devcontainer/base.Dockerfile
|
||||
|
||||
# [Choice] Python version: 3, 3.9, 3.8, 3.7, 3.6
|
||||
ARG VARIANT="3"
|
||||
FROM mcr.microsoft.com/devcontainers/python:0-${VARIANT}
|
||||
|
||||
# [Optional] If your pip requirements rarely change, uncomment this section to add them to the image.
|
||||
# COPY requirements.txt /tmp/pip-tmp/
|
||||
# RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \
|
||||
# && rm -rf /tmp/pip-tmp
|
||||
|
||||
# [Optional] Uncomment this section to install additional OS packages.
|
||||
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
||||
# && apt-get -y install --no-install-recommends <your-package-list-here>
|
||||
|
||||
# [Optional] Uncomment this line to install global node packages.
|
||||
# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g <your-package-here>" 2>&1
|
60
.devcontainer/devcontainer.json
Normal file
@ -0,0 +1,60 @@
|
||||
{
|
||||
"name": "Python 3",
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile",
|
||||
"context": "..",
|
||||
"args": {
|
||||
// Update 'VARIANT' to pick a Python version: 3, 3.6, 3.7, 3.8, 3.9
|
||||
"VARIANT": "3"
|
||||
}
|
||||
},
|
||||
|
||||
// To give the container access to a device serial port, you can uncomment one of the following lines.
|
||||
// Note: If running on Windows, you will have to do some additional steps:
|
||||
// https://stackoverflow.com/questions/68527888/how-can-i-use-a-usb-com-port-inside-of-a-vscode-development-container
|
||||
//
|
||||
// You can explicitly just forward the port you want to connect to. Replace `/dev/ttyACM0` with the serial port for
|
||||
// your device. This will only work if the device is plugged in from the start without reconnecting. Adding the
|
||||
// `dialout` group is needed if read/write permisions for the port are limitted to the dialout user.
|
||||
// "runArgs": ["--device=/dev/ttyACM0", "--group-add", "dialout"],
|
||||
//
|
||||
// Alternatively, you can give more comprehensive access to the host system. This will expose all the host devices to
|
||||
// the container. Adding the `dialout` group is needed if read/write permisions for the port are limitted to the
|
||||
// dialout user. This could allow the container to modify unrelated serial devices, which would be a similar level of
|
||||
// risk to running the build directly on the host.
|
||||
// "runArgs": ["--privileged", "-v", "/dev/bus/usb:/dev/bus/usb", "--group-add", "dialout"],
|
||||
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {
|
||||
"terminal.integrated.shell.linux": "/bin/bash",
|
||||
"python.pythonPath": "/usr/local/bin/python",
|
||||
"python.linting.enabled": true,
|
||||
"python.linting.pylintEnabled": true,
|
||||
"python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
|
||||
"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
|
||||
"python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
|
||||
"python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
|
||||
"python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
|
||||
"python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
|
||||
"python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
|
||||
"python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
|
||||
"python.linting.pylintPath": "/usr/local/py-utils/bin/pylint"
|
||||
},
|
||||
"extensions": [
|
||||
"ms-python.python",
|
||||
"platformio.platformio-ide"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [],
|
||||
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
"postCreateCommand": "bash -i -c 'nvm install && npm ci'",
|
||||
|
||||
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
||||
"remoteUser": "vscode"
|
||||
}
|
||||
|
2
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
github: [Aircoookie,blazoncek]
|
||||
custom: ['https://paypal.me/Aircoookie','https://paypal.me/blazoncek']
|
86
.github/ISSUE_TEMPLATE/bug.yml
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
name: Bug Report
|
||||
description: File a bug report
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please quickly search existing issues first before submitting a bug.
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: A clear and concise description of what the bug is.
|
||||
placeholder: Tell us what the problem is.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: how-to-reproduce
|
||||
attributes:
|
||||
label: To Reproduce Bug
|
||||
description: Steps to reproduce the behavior, if consistently possible.
|
||||
placeholder: Tell us how to make the bug appear.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected-behavior
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: A clear and concise description of what you expected to happen.
|
||||
placeholder: Tell us what you expected to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: install_format
|
||||
attributes:
|
||||
label: Install Method
|
||||
description: How did you install WLED?
|
||||
options:
|
||||
- Binary from WLED.me
|
||||
- Self-Compiled
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: What version of WLED?
|
||||
description: You can find this in by going to Config -> Security & Updates -> Scroll to Bottom. Copy and paste the entire line after "Server message"
|
||||
placeholder: "e.g. WLED 0.13.1 (build 2203150)"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: Board
|
||||
attributes:
|
||||
label: Which microcontroller/board are you seeing the problem on?
|
||||
multiple: true
|
||||
options:
|
||||
- ESP8266
|
||||
- ESP32
|
||||
- ESP32-S3
|
||||
- ESP32-S2
|
||||
- ESP32-C3
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Relevant log/trace output
|
||||
description: Please copy and paste any relevant log output if you have it. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Anything else?
|
||||
description: |
|
||||
Links? References? Anything that will give us more context about the issue you are encountering!
|
||||
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
|
||||
validations:
|
||||
required: false
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/wled-dev/WLED/blob/main/CODE_OF_CONDUCT.md)
|
||||
options:
|
||||
- label: I agree to follow this project's Code of Conduct
|
||||
required: true
|
11
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: WLED Discord community
|
||||
url: https://discord.gg/KuqP7NE
|
||||
about: Please ask and answer questions and discuss setup issues here!
|
||||
- name: WLED community forum
|
||||
url: https://wled.discourse.group/
|
||||
about: For issues and ideas that might need longer discussion.
|
||||
- name: kno.wled.ge base
|
||||
url: https://kno.wled.ge/basics/faq/
|
||||
about: Take a look at the frequently asked questions and documentation, perhaps your question is already answered!
|
22
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an improvement idea for WLED!
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
||||
Thank you for your ideas for making WLED better!
|
20
.github/stale.yml
vendored
@ -1,20 +0,0 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 120
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- pinned
|
||||
- keep
|
||||
- enhancement
|
||||
- confirmed
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
Hey! This issue has been open for quite some time without any new comments now.
|
||||
It will be closed automatically in a week if no further activity occurs.
|
||||
|
||||
Thank you for using WLED!
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
82
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
name: WLED Build
|
||||
|
||||
# Only included into other workflows
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
|
||||
get_default_envs:
|
||||
name: Gather Environments
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
cache: 'pip'
|
||||
- name: Install PlatformIO
|
||||
run: pip install -r requirements.txt
|
||||
- name: Get default environments
|
||||
id: envs
|
||||
run: |
|
||||
echo "environments=$(pio project config --json-output | jq -cr '.[0][1][0][1]')" >> $GITHUB_OUTPUT
|
||||
outputs:
|
||||
environments: ${{ steps.envs.outputs.environments }}
|
||||
|
||||
|
||||
build:
|
||||
name: Build Environments
|
||||
runs-on: ubuntu-latest
|
||||
needs: get_default_envs
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
- run: npm ci
|
||||
- name: Cache PlatformIO
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.platformio/.cache
|
||||
~/.buildcache
|
||||
build_output
|
||||
key: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-${{ hashFiles('wled00/**', 'usermods/**') }}
|
||||
restore-keys: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
cache: 'pip'
|
||||
- name: Install PlatformIO
|
||||
run: pip install -r requirements.txt
|
||||
|
||||
- name: Build firmware
|
||||
run: pio run -e ${{ matrix.environment }}
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: firmware-${{ matrix.environment }}
|
||||
path: |
|
||||
build_output/release/*.bin
|
||||
build_output/release/*_ESP02*.bin.gz
|
||||
|
||||
|
||||
testCdata:
|
||||
name: Test cdata.js
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
- run: npm ci
|
||||
- run: npm test
|
47
.github/workflows/nightly.yml
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
|
||||
name: Deploy Nightly
|
||||
on:
|
||||
# This can be used to automatically publish nightlies at UTC nighttime
|
||||
schedule:
|
||||
- cron: '0 2 * * *' # run at 2 AM UTC
|
||||
# This can be used to allow manually triggering nightlies from the web interface
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
wled_build:
|
||||
uses: ./.github/workflows/build.yml
|
||||
nightly:
|
||||
name: Deploy nightly
|
||||
runs-on: ubuntu-latest
|
||||
needs: wled_build
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
merge-multiple: true
|
||||
- name: Show Files
|
||||
run: ls -la
|
||||
- name: "✏️ Generate release changelog"
|
||||
id: changelog
|
||||
uses: janheinrichmerker/action-github-changelog-generator@v2.3
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
sinceTag: v0.15.0
|
||||
- name: Update Nightly Release
|
||||
uses: andelf/nightly-release@main
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: nightly
|
||||
name: 'Nightly Release $$'
|
||||
prerelease: true
|
||||
body: ${{ steps.changelog.outputs.changelog }}
|
||||
files: |
|
||||
*.bin
|
||||
*.bin.gz
|
||||
- name: Repository Dispatch
|
||||
uses: peter-evans/repository-dispatch@v3
|
||||
with:
|
||||
repository: wled/WLED-WebInstaller
|
||||
event-type: release-nightly
|
||||
token: ${{ secrets.PAT_PUBLIC }}
|
16
.github/workflows/pr-merge.yaml
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
name: Notify Discord on PR Merge
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
|
||||
jobs:
|
||||
notify:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send Discord notification
|
||||
shell: bash
|
||||
env:
|
||||
DISCORD_WEBHOOK_BETA_TESTERS: ${{ secrets.DISCORD_WEBHOOK_BETA_TESTERS }}
|
||||
if: github.event.pull_request.merged == true
|
||||
run: |
|
||||
curl -H "Content-Type: application/json" -d '{"content": "Pull Request #{{ github.event.pull_request.number }} merged by {{ github.actor }}"}' $DISCORD_WEBHOOK_BETA_TESTERS
|
35
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
name: WLED Release CI
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
|
||||
wled_build:
|
||||
uses: ./.github/workflows/build.yml
|
||||
|
||||
release:
|
||||
name: Create Release
|
||||
runs-on: ubuntu-latest
|
||||
needs: wled_build
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
merge-multiple: true
|
||||
- name: "✏️ Generate release changelog"
|
||||
id: changelog
|
||||
uses: janheinrichmerker/action-github-changelog-generator@v2.3
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
sinceTag: v0.15.0
|
||||
- name: Create draft release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
body: ${{ steps.changelog.outputs.changelog }}
|
||||
draft: True
|
||||
files: |
|
||||
*.bin
|
||||
*.bin.gz
|
||||
|
30
.github/workflows/stale.yml
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
name: 'Close stale issues and PRs'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 12 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
days-before-stale: 120
|
||||
days-before-close: 7
|
||||
stale-issue-label: 'stale'
|
||||
stale-pr-label: 'stale'
|
||||
exempt-issue-labels: 'pinned,keep,enhancement,confirmed'
|
||||
exempt-pr-labels: 'pinned,keep,enhancement,confirmed'
|
||||
exempt-all-milestones: true
|
||||
operations-per-run: 1000
|
||||
stale-issue-message: >
|
||||
Hey! This issue has been open for quite some time without any new comments now.
|
||||
It will be closed automatically in a week if no further activity occurs.
|
||||
|
||||
Thank you for using WLED! ✨
|
||||
stale-pr-message: >
|
||||
Hey! This pull request has been open for quite some time without any new comments now.
|
||||
It will be closed automatically in a week if no further activity occurs.
|
||||
|
||||
Thank you for contributing to WLED! ❤️
|
13
.github/workflows/test.yaml
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
dispatch:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Repository Dispatch
|
||||
uses: peter-evans/repository-dispatch@v3
|
||||
with:
|
||||
repository: wled/WLED-WebInstaller
|
||||
event-type: release-nightly
|
||||
token: ${{ secrets.PAT_PUBLIC }}
|
11
.github/workflows/wled-ci.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
name: WLED CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
wled_build:
|
||||
uses: ./.github/workflows/build.yml
|
26
.gitignore
vendored
@ -1,11 +1,25 @@
|
||||
.cache
|
||||
.clang-format
|
||||
.direnv
|
||||
.DS_Store
|
||||
.idea
|
||||
.pio
|
||||
.pioenvs
|
||||
.piolibdeps
|
||||
.vscode
|
||||
!.vscode/extensions.json
|
||||
/wled00/Release
|
||||
|
||||
esp01-update.sh
|
||||
platformio_override.ini
|
||||
replace_fs.py
|
||||
wled-update.sh
|
||||
|
||||
/build_output/
|
||||
/node_modules/
|
||||
/logs/
|
||||
|
||||
/wled00/extLibs
|
||||
/platformio_override.ini
|
||||
.DS_Store
|
||||
.gitignore
|
||||
.clang-format
|
||||
/wled00/LittleFS
|
||||
/wled00/my_config.h
|
||||
/wled00/Release
|
||||
/wled00/wled00.ino.cpp
|
||||
/wled00/html_*.h
|
||||
|
3
.gitpod.Dockerfile
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
FROM gitpod/workspace-full
|
||||
|
||||
USER gitpod
|
11
.gitpod.yml
Normal file
@ -0,0 +1,11 @@
|
||||
tasks:
|
||||
- command: pip3 install -U platformio && platformio run
|
||||
|
||||
image:
|
||||
file: .gitpod.Dockerfile
|
||||
|
||||
vscode:
|
||||
extensions:
|
||||
- Atishay-Jain.All-Autocomplete
|
||||
- esbenp.prettier-vscode
|
||||
- shardulm94.trailing-spaces
|
43
.travis.yml
@ -1,43 +0,0 @@
|
||||
# Continuous Integration (CI) is the practice, in software
|
||||
# engineering, of merging all developer working copies with a shared mainline
|
||||
# several times a day < https://docs.platformio.org/page/ci/index.html >
|
||||
#
|
||||
# Documentation:
|
||||
#
|
||||
# * Travis CI Embedded Builds with PlatformIO
|
||||
# < https://docs.travis-ci.com/user/integration/platformio/ >
|
||||
#
|
||||
# * PlatformIO integration with Travis CI
|
||||
# < https://docs.platformio.org/page/ci/travis.html >
|
||||
#
|
||||
# * User Guide for `platformio ci` command
|
||||
# < https://docs.platformio.org/page/userguide/cmd_ci.html >
|
||||
#
|
||||
#
|
||||
# Please choose one of the following templates (proposed below) and uncomment
|
||||
# it (remove "# " before each line) or use own configuration according to the
|
||||
# Travis CI documentation (see above).
|
||||
#
|
||||
# * Test the Travis config here:
|
||||
# < https://config.travis-ci.com/explore >
|
||||
#
|
||||
|
||||
language: python
|
||||
python:
|
||||
# - "2.7"
|
||||
- "3.5"
|
||||
os: linux
|
||||
cache:
|
||||
bundler: true
|
||||
ccache: true
|
||||
directories:
|
||||
- "~/.platformio"
|
||||
- "~/.buildcache"
|
||||
env:
|
||||
- PLATFORMIO_CI_SRC=wled00
|
||||
install:
|
||||
- pip install -U platformio
|
||||
- platformio update
|
||||
script:
|
||||
# - platformio ci --project-conf=./platformio.ini
|
||||
- platformio run
|
17
.vscode/extensions.json
vendored
@ -1,7 +1,10 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
]
|
||||
}
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
"ms-vscode.cpptools-extension-pack"
|
||||
]
|
||||
}
|
||||
|
42
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Build: HTML and binary",
|
||||
"dependsOn": [
|
||||
"Build: HTML only",
|
||||
"Build: binary only"
|
||||
],
|
||||
"dependsOrder": "sequence",
|
||||
"problemMatcher": [
|
||||
"$platformio"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "PlatformIO",
|
||||
"label": "Build: binary only",
|
||||
"task": "Build",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$platformio"
|
||||
],
|
||||
"presentation": {
|
||||
"panel": "shared"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "build",
|
||||
"group": "build",
|
||||
"problemMatcher": [],
|
||||
"label": "Build: HTML only",
|
||||
"detail": "npm run build",
|
||||
"presentation": {
|
||||
"panel": "shared"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
1519
CHANGELOG.md
76
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,76 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at dev.aircoookie@gmail.com. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
108
CONTRIBUTING.md
Normal file
@ -0,0 +1,108 @@
|
||||
## Thank you for making WLED better!
|
||||
|
||||
Here are a few suggestions to make it easier for you to contribute!
|
||||
|
||||
### Describe your PR
|
||||
|
||||
Please add a description of your proposed code changes. It does not need to be an exhaustive essay, however a PR with no description or just a few words might not get accepted, simply because very basic information is missing.
|
||||
|
||||
A good description helps us to review and understand your proposed changes. For example, you could say a few words about
|
||||
* what you try to achieve (new feature, fixing a bug, refactoring, security enhancements, etc.)
|
||||
* how your code works (short technical summary - focus on important aspects that might not be obvious when reading the code)
|
||||
* testing you performed, known limitations, open ends you possibly could not solve.
|
||||
* any areas where you like to get help from an experienced maintainer (yes WLED has become big 😉)
|
||||
|
||||
### Target branch for pull requests
|
||||
|
||||
Please make all PRs against the `main` branch.
|
||||
|
||||
### Updating your code
|
||||
While the PR is open - and under review by maintainers - you may be asked to modify your PR source code.
|
||||
You can simply update your own branch, and push changes in response to reviewer recommendations.
|
||||
Github will pick up the changes so your PR stays up-to-date.
|
||||
|
||||
> [!CAUTION]
|
||||
> Do not use "force-push" while your PR is open!
|
||||
> It has many subtle and unexpected consequences on our github reposistory.
|
||||
> For example, we regularly lost review comments when the PR author force-pushes code changes. So, pretty please, do not force-push.
|
||||
|
||||
|
||||
You can find a collection of very useful tips and tricks here: https://github.com/wled-dev/WLED/wiki/How-to-properly-submit-a-PR
|
||||
|
||||
|
||||
### Code style
|
||||
|
||||
When in doubt, it is easiest to replicate the code style you find in the files you want to edit :)
|
||||
Below are the guidelines we use in the WLED repository.
|
||||
|
||||
#### Indentation
|
||||
|
||||
We use tabs for Indentation in Web files (.html/.css/.js) and spaces (2 per indentation level) for all other files.
|
||||
You are all set if you have enabled `Editor: Detect Indentation` in VS Code.
|
||||
|
||||
#### Blocks
|
||||
|
||||
Whether the opening bracket of e.g. an `if` block is in the same line as the condition or in a separate line is up to your discretion. If there is only one statement, leaving out block brackets is acceptable.
|
||||
|
||||
Good:
|
||||
```cpp
|
||||
if (a == b) {
|
||||
doStuff(a);
|
||||
}
|
||||
```
|
||||
|
||||
```cpp
|
||||
if (a == b) doStuff(a);
|
||||
```
|
||||
|
||||
Acceptable - however the first variant is usually easier to read:
|
||||
```cpp
|
||||
if (a == b)
|
||||
{
|
||||
doStuff(a);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
There should always be a space between a keyword and its condition and between the condition and brace.
|
||||
Within the condition, no space should be between the parenthesis and variables.
|
||||
Spaces between variables and operators are up to the authors discretion.
|
||||
There should be no space between function names and their argument parenthesis.
|
||||
|
||||
Good:
|
||||
```cpp
|
||||
if (a == b) {
|
||||
doStuff(a);
|
||||
}
|
||||
```
|
||||
|
||||
Not good:
|
||||
```cpp
|
||||
if( a==b ){
|
||||
doStuff ( a);
|
||||
}
|
||||
```
|
||||
|
||||
#### Comments
|
||||
|
||||
Comments should have a space between the delimiting characters (e.g. `//`) and the comment text.
|
||||
Note: This is a recent change, the majority of the codebase still has comments without spaces.
|
||||
|
||||
Good:
|
||||
```
|
||||
// This is a comment.
|
||||
|
||||
/* This is a CSS inline comment */
|
||||
|
||||
/*
|
||||
* This is a comment
|
||||
* wrapping over multiple lines,
|
||||
* used in WLED for file headers and function explanations
|
||||
*/
|
||||
|
||||
<!-- This is an HTML comment -->
|
||||
```
|
||||
|
||||
There is no hard character limit for a comment within a line,
|
||||
though as a rule of thumb consider wrapping after 120 characters.
|
||||
Inline comments are OK if they describe that line only and are not exceedingly wide.
|
307
LICENSE
@ -1,21 +1,294 @@
|
||||
MIT License
|
||||
Copyright (c) 2016-present Christian Schwinne and individual WLED contributors
|
||||
Licensed under the EUPL v. 1.2 or later
|
||||
|
||||
Copyright (c) 2016 Christian Schwinne
|
||||
EUROPEAN UNION PUBLIC LICENCE v. 1.2
|
||||
EUPL © the European Union 2007, 2016
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
This European Union Public Licence (the ‘EUPL’) applies to the Work (as
|
||||
defined below) which is provided under the terms of this Licence. Any use of
|
||||
the Work, other than as authorised under this Licence is prohibited (to the
|
||||
extent such use is covered by a right of the copyright holder of the Work).
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
The Work is provided under the terms of this Licence when the Licensor (as
|
||||
defined below) has placed the following notice immediately following the
|
||||
copyright notice for the Work:
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Licensed under the EUPL
|
||||
|
||||
or has expressed by any other means his willingness to license under the EUPL.
|
||||
|
||||
1. Definitions
|
||||
|
||||
In this Licence, the following terms have the following meaning:
|
||||
|
||||
- ‘The Licence’: this Licence.
|
||||
|
||||
- ‘The Original Work’: the work or software distributed or communicated by the
|
||||
Licensor under this Licence, available as Source Code and also as Executable
|
||||
Code as the case may be.
|
||||
|
||||
- ‘Derivative Works’: the works or software that could be created by the
|
||||
Licensee, based upon the Original Work or modifications thereof. This
|
||||
Licence does not define the extent of modification or dependence on the
|
||||
Original Work required in order to classify a work as a Derivative Work;
|
||||
this extent is determined by copyright law applicable in the country
|
||||
mentioned in Article 15.
|
||||
|
||||
- ‘The Work’: the Original Work or its Derivative Works.
|
||||
|
||||
- ‘The Source Code’: the human-readable form of the Work which is the most
|
||||
convenient for people to study and modify.
|
||||
|
||||
- ‘The Executable Code’: any code which has generally been compiled and which
|
||||
is meant to be interpreted by a computer as a program.
|
||||
|
||||
- ‘The Licensor’: the natural or legal person that distributes or communicates
|
||||
the Work under the Licence.
|
||||
|
||||
- ‘Contributor(s)’: any natural or legal person who modifies the Work under
|
||||
the Licence, or otherwise contributes to the creation of a Derivative Work.
|
||||
|
||||
- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of
|
||||
the Work under the terms of the Licence.
|
||||
|
||||
- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending,
|
||||
renting, distributing, communicating, transmitting, or otherwise making
|
||||
available, online or offline, copies of the Work or providing access to its
|
||||
essential functionalities at the disposal of any other natural or legal
|
||||
person.
|
||||
|
||||
2. Scope of the rights granted by the Licence
|
||||
|
||||
The Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
|
||||
sublicensable licence to do the following, for the duration of copyright
|
||||
vested in the Original Work:
|
||||
|
||||
- use the Work in any circumstance and for all usage,
|
||||
- reproduce the Work,
|
||||
- modify the Work, and make Derivative Works based upon the Work,
|
||||
- communicate to the public, including the right to make available or display
|
||||
the Work or copies thereof to the public and perform publicly, as the case
|
||||
may be, the Work,
|
||||
- distribute the Work or copies thereof,
|
||||
- lend and rent the Work or copies thereof,
|
||||
- sublicense rights in the Work or copies thereof.
|
||||
|
||||
Those rights can be exercised on any media, supports and formats, whether now
|
||||
known or later invented, as far as the applicable law permits so.
|
||||
|
||||
In the countries where moral rights apply, the Licensor waives his right to
|
||||
exercise his moral right to the extent allowed by law in order to make
|
||||
effective the licence of the economic rights here above listed.
|
||||
|
||||
The Licensor grants to the Licensee royalty-free, non-exclusive usage rights
|
||||
to any patents held by the Licensor, to the extent necessary to make use of
|
||||
the rights granted on the Work under this Licence.
|
||||
|
||||
3. Communication of the Source Code
|
||||
|
||||
The Licensor may provide the Work either in its Source Code form, or as
|
||||
Executable Code. If the Work is provided as Executable Code, the Licensor
|
||||
provides in addition a machine-readable copy of the Source Code of the Work
|
||||
along with each copy of the Work that the Licensor distributes or indicates,
|
||||
in a notice following the copyright notice attached to the Work, a repository
|
||||
where the Source Code is easily and freely accessible for as long as the
|
||||
Licensor continues to distribute or communicate the Work.
|
||||
|
||||
4. Limitations on copyright
|
||||
|
||||
Nothing in this Licence is intended to deprive the Licensee of the benefits
|
||||
from any exception or limitation to the exclusive rights of the rights owners
|
||||
in the Work, of the exhaustion of those rights or of other applicable
|
||||
limitations thereto.
|
||||
|
||||
5. Obligations of the Licensee
|
||||
|
||||
The grant of the rights mentioned above is subject to some restrictions and
|
||||
obligations imposed on the Licensee. Those obligations are the following:
|
||||
|
||||
Attribution right: The Licensee shall keep intact all copyright, patent or
|
||||
trademarks notices and all notices that refer to the Licence and to the
|
||||
disclaimer of warranties. The Licensee must include a copy of such notices and
|
||||
a copy of the Licence with every copy of the Work he/she distributes or
|
||||
communicates. The Licensee must cause any Derivative Work to carry prominent
|
||||
notices stating that the Work has been modified and the date of modification.
|
||||
|
||||
Copyleft clause: If the Licensee distributes or communicates copies of the
|
||||
Original Works or Derivative Works, this Distribution or Communication will be
|
||||
done under the terms of this Licence or of a later version of this Licence
|
||||
unless the Original Work is expressly distributed only under this version of
|
||||
the Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee
|
||||
(becoming Licensor) cannot offer or impose any additional terms or conditions
|
||||
on the Work or Derivative Work that alter or restrict the terms of the
|
||||
Licence.
|
||||
|
||||
Compatibility clause: If the Licensee Distributes or Communicates Derivative
|
||||
Works or copies thereof based upon both the Work and another work licensed
|
||||
under a Compatible Licence, this Distribution or Communication can be done
|
||||
under the terms of this Compatible Licence. For the sake of this clause,
|
||||
‘Compatible Licence’ refers to the licences listed in the appendix attached to
|
||||
this Licence. Should the Licensee's obligations under the Compatible Licence
|
||||
conflict with his/her obligations under this Licence, the obligations of the
|
||||
Compatible Licence shall prevail.
|
||||
|
||||
Provision of Source Code: When distributing or communicating copies of the
|
||||
Work, the Licensee will provide a machine-readable copy of the Source Code or
|
||||
indicate a repository where this Source will be easily and freely available
|
||||
for as long as the Licensee continues to distribute or communicate the Work.
|
||||
|
||||
Legal Protection: This Licence does not grant permission to use the trade
|
||||
names, trademarks, service marks, or names of the Licensor, except as required
|
||||
for reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the copyright notice.
|
||||
|
||||
6. Chain of Authorship
|
||||
|
||||
The original Licensor warrants that the copyright in the Original Work granted
|
||||
hereunder is owned by him/her or licensed to him/her and that he/she has the
|
||||
power and authority to grant the Licence.
|
||||
|
||||
Each Contributor warrants that the copyright in the modifications he/she
|
||||
brings to the Work are owned by him/her or licensed to him/her and that he/she
|
||||
has the power and authority to grant the Licence.
|
||||
|
||||
Each time You accept the Licence, the original Licensor and subsequent
|
||||
Contributors grant You a licence to their contributions to the Work, under the
|
||||
terms of this Licence.
|
||||
|
||||
7. Disclaimer of Warranty
|
||||
|
||||
The Work is a work in progress, which is continuously improved by numerous
|
||||
Contributors. It is not a finished work and may therefore contain defects or
|
||||
‘bugs’ inherent to this type of development.
|
||||
|
||||
For the above reason, the Work is provided under the Licence on an ‘as is’
|
||||
basis and without warranties of any kind concerning the Work, including
|
||||
without limitation merchantability, fitness for a particular purpose, absence
|
||||
of defects or errors, accuracy, non-infringement of intellectual property
|
||||
rights other than copyright as stated in Article 6 of this Licence.
|
||||
|
||||
This disclaimer of warranty is an essential part of the Licence and a
|
||||
condition for the grant of any rights to the Work.
|
||||
|
||||
8. Disclaimer of Liability
|
||||
|
||||
Except in the cases of wilful misconduct or damages directly caused to natural
|
||||
persons, the Licensor will in no event be liable for any direct or indirect,
|
||||
material or moral, damages of any kind, arising out of the Licence or of the
|
||||
use of the Work, including without limitation, damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, loss of data or any commercial
|
||||
damage, even if the Licensor has been advised of the possibility of such
|
||||
damage. However, the Licensor will be liable under statutory product liability
|
||||
laws as far such laws apply to the Work.
|
||||
|
||||
9. Additional agreements
|
||||
|
||||
While distributing the Work, You may choose to conclude an additional
|
||||
agreement, defining obligations or services consistent with this Licence.
|
||||
However, if accepting obligations, You may act only on your own behalf and on
|
||||
your sole responsibility, not on behalf of the original Licensor or any other
|
||||
Contributor, and only if You agree to indemnify, defend, and hold each
|
||||
Contributor harmless for any liability incurred by, or claims asserted against
|
||||
such Contributor by the fact You have accepted any warranty or additional
|
||||
liability.
|
||||
|
||||
10. Acceptance of the Licence
|
||||
|
||||
The provisions of this Licence can be accepted by clicking on an icon ‘I
|
||||
agree’ placed under the bottom of a window displaying the text of this Licence
|
||||
or by affirming consent in any other similar way, in accordance with the rules
|
||||
of applicable law. Clicking on that icon indicates your clear and irrevocable
|
||||
acceptance of this Licence and all of its terms and conditions.
|
||||
|
||||
Similarly, you irrevocably accept this Licence and all of its terms and
|
||||
conditions by exercising any rights granted to You by Article 2 of this
|
||||
Licence, such as the use of the Work, the creation by You of a Derivative Work
|
||||
or the Distribution or Communication by You of the Work or copies thereof.
|
||||
|
||||
11. Information to the public
|
||||
|
||||
In case of any Distribution or Communication of the Work by means of
|
||||
electronic communication by You (for example, by offering to download the Work
|
||||
from a remote location) the distribution channel or media (for example, a
|
||||
website) must at least provide to the public the information requested by the
|
||||
applicable law regarding the Licensor, the Licence and the way it may be
|
||||
accessible, concluded, stored and reproduced by the Licensee.
|
||||
|
||||
12. Termination of the Licence
|
||||
|
||||
The Licence and the rights granted hereunder will terminate automatically upon
|
||||
any breach by the Licensee of the terms of the Licence.
|
||||
|
||||
Such a termination will not terminate the licences of any person who has
|
||||
received the Work from the Licensee under the Licence, provided such persons
|
||||
remain in full compliance with the Licence.
|
||||
|
||||
13. Miscellaneous
|
||||
|
||||
Without prejudice of Article 9 above, the Licence represents the complete
|
||||
agreement between the Parties as to the Work.
|
||||
|
||||
If any provision of the Licence is invalid or unenforceable under applicable
|
||||
law, this will not affect the validity or enforceability of the Licence as a
|
||||
whole. Such provision will be construed or reformed so as necessary to make it
|
||||
valid and enforceable.
|
||||
|
||||
The European Commission may publish other linguistic versions or new versions
|
||||
of this Licence or updated versions of the Appendix, so far this is required
|
||||
and reasonable, without reducing the scope of the rights granted by the
|
||||
Licence. New versions of the Licence will be published with a unique version
|
||||
number.
|
||||
|
||||
All linguistic versions of this Licence, approved by the European Commission,
|
||||
have identical value. Parties can take advantage of the linguistic version of
|
||||
their choice.
|
||||
|
||||
14. Jurisdiction
|
||||
|
||||
Without prejudice to specific agreement between parties,
|
||||
|
||||
- any litigation resulting from the interpretation of this License, arising
|
||||
between the European Union institutions, bodies, offices or agencies, as a
|
||||
Licensor, and any Licensee, will be subject to the jurisdiction of the Court
|
||||
of Justice of the European Union, as laid down in article 272 of the Treaty
|
||||
on the Functioning of the European Union,
|
||||
|
||||
- any litigation arising between other parties and resulting from the
|
||||
interpretation of this License, will be subject to the exclusive
|
||||
jurisdiction of the competent court where the Licensor resides or conducts
|
||||
its primary business.
|
||||
|
||||
15. Applicable Law
|
||||
|
||||
Without prejudice to specific agreement between parties,
|
||||
|
||||
- this Licence shall be governed by the law of the European Union Member State
|
||||
where the Licensor has his seat, resides or has his registered office,
|
||||
|
||||
- this licence shall be governed by Belgian law if the Licensor has no seat,
|
||||
residence or registered office inside a European Union Member State.
|
||||
|
||||
Appendix
|
||||
|
||||
‘Compatible Licences’ according to Article 5 EUPL are:
|
||||
|
||||
- GNU General Public License (GPL) v. 2, v. 3
|
||||
- GNU Affero General Public License (AGPL) v. 3
|
||||
- Open Software License (OSL) v. 2.1, v. 3.0
|
||||
- Eclipse Public License (EPL) v. 1.0
|
||||
- CeCILL v. 2.0, v. 2.1
|
||||
- Mozilla Public Licence (MPL) v. 2
|
||||
- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3
|
||||
- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for
|
||||
works other than software
|
||||
- European Union Public Licence (EUPL) v. 1.1, v. 1.2
|
||||
- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong
|
||||
Reciprocity (LiLiQ-R+).
|
||||
|
||||
The European Commission may update this Appendix to later versions of the
|
||||
above licences without producing a new version of the EUPL, as long as they
|
||||
provide the rights granted in Article 2 of this Licence and protect the
|
||||
covered Source Code from exclusive appropriation.
|
||||
|
||||
All other changes or additions to this Appendix require the production of a
|
||||
new EUPL version.
|
47
boards/lolin_s3_mini.json
Normal file
@ -0,0 +1,47 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"memory_type": "qio_qspi"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_LOLIN_S3_MINI",
|
||||
"-DARDUINO_USB_MODE=1"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"hwids": [
|
||||
[
|
||||
"0x303A",
|
||||
"0x8167"
|
||||
]
|
||||
],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "lolin_s3_mini"
|
||||
},
|
||||
"connectivity": [
|
||||
"bluetooth",
|
||||
"wifi"
|
||||
],
|
||||
"debug": {
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "WEMOS LOLIN S3 Mini",
|
||||
"upload": {
|
||||
"flash_size": "4MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 4194304,
|
||||
"require_upload_port": true,
|
||||
"speed": 460800
|
||||
},
|
||||
"url": "https://www.wemos.cc/en/latest/s3/index.html",
|
||||
"vendor": "WEMOS"
|
||||
}
|
||||
|
5
images/Readme.md
Normal file
@ -0,0 +1,5 @@
|
||||
### Additional Logos
|
||||
|
||||
Additional awesome logos for WLED can be found here [Aircoookie/Akemi](https://github.com/Aircoookie/Akemi).
|
||||
|
||||
<img src="https://github.com/Aircoookie/Akemi/blob/master/akemi/001_cheerful.png">
|
BIN
images/macbook-pro-space-gray-on-the-wooden-table.jpg
Normal file
After Width: | Height: | Size: 489 KiB |
BIN
images/walking-with-iphone-x.jpg
Normal file
After Width: | Height: | Size: 500 KiB |
BIN
images/wled_logo_akemi.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
images/wled_logo_old.png
Normal file
After Width: | Height: | Size: 40 KiB |
504
lib/ESP8266PWM/src/core_esp8266_waveform_phase.cpp
Normal file
@ -0,0 +1,504 @@
|
||||
/* esp8266_waveform imported from platform source code
|
||||
Modified for WLED to work around a fault in the NMI handling,
|
||||
which can result in the system locking up and hard WDT crashes.
|
||||
|
||||
Imported from https://github.com/esp8266/Arduino/blob/7e0d20e2b9034994f573a236364e0aef17fd66de/cores/esp8266/core_esp8266_waveform_phase.cpp
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
esp8266_waveform - General purpose waveform generation and control,
|
||||
supporting outputs on all pins in parallel.
|
||||
|
||||
Copyright (c) 2018 Earle F. Philhower, III. All rights reserved.
|
||||
Copyright (c) 2020 Dirk O. Kaar.
|
||||
|
||||
The core idea is to have a programmable waveform generator with a unique
|
||||
high and low period (defined in microseconds or CPU clock cycles). TIMER1 is
|
||||
set to 1-shot mode and is always loaded with the time until the next edge
|
||||
of any live waveforms.
|
||||
|
||||
Up to one waveform generator per pin supported.
|
||||
|
||||
Each waveform generator is synchronized to the ESP clock cycle counter, not the
|
||||
timer. This allows for removing interrupt jitter and delay as the counter
|
||||
always increments once per 80MHz clock. Changes to a waveform are
|
||||
contiguous and only take effect on the next waveform transition,
|
||||
allowing for smooth transitions.
|
||||
|
||||
This replaces older tone(), analogWrite(), and the Servo classes.
|
||||
|
||||
Everywhere in the code where "ccy" or "ccys" is used, it means ESP.getCycleCount()
|
||||
clock cycle time, or an interval measured in clock cycles, but not TIMER1
|
||||
cycles (which may be 2 CPU clock cycles @ 160MHz).
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "core_esp8266_waveform.h"
|
||||
#include <Arduino.h>
|
||||
#include "debug.h"
|
||||
#include "ets_sys.h"
|
||||
#include <atomic>
|
||||
|
||||
|
||||
// ----- @willmmiles begin patch -----
|
||||
// Linker magic
|
||||
extern "C" void usePWMFixedNMI(void) {};
|
||||
|
||||
// NMI crash workaround
|
||||
// Sometimes the NMI fails to return, stalling the CPU. When this happens,
|
||||
// the next NMI gets a return address /inside the NMI handler function/.
|
||||
// We work around this by caching the last NMI return address, and restoring
|
||||
// the epc3 and eps3 registers to the previous values if the observed epc3
|
||||
// happens to be pointing to the _NMILevelVector function.
|
||||
extern "C" void _NMILevelVector();
|
||||
extern "C" void _UserExceptionVector_1(); // the next function after _NMILevelVector
|
||||
static inline IRAM_ATTR void nmiCrashWorkaround() {
|
||||
static uintptr_t epc3_backup, eps3_backup;
|
||||
|
||||
uintptr_t epc3, eps3;
|
||||
__asm__ __volatile__("rsr %0,epc3; rsr %1,eps3":"=a"(epc3),"=a" (eps3));
|
||||
if ((epc3 < (uintptr_t) &_NMILevelVector) || (epc3 >= (uintptr_t) &_UserExceptionVector_1)) {
|
||||
// Address is good; save backup
|
||||
epc3_backup = epc3;
|
||||
eps3_backup = eps3;
|
||||
} else {
|
||||
// Address is inside the NMI handler -- restore from backup
|
||||
__asm__ __volatile__("wsr %0,epc3; wsr %1,eps3"::"a"(epc3_backup),"a"(eps3_backup));
|
||||
}
|
||||
}
|
||||
// ----- @willmmiles end patch -----
|
||||
|
||||
|
||||
// No-op calls to override the PWM implementation
|
||||
extern "C" void _setPWMFreq_weak(uint32_t freq) { (void) freq; }
|
||||
extern "C" IRAM_ATTR bool _stopPWM_weak(int pin) { (void) pin; return false; }
|
||||
extern "C" bool _setPWM_weak(int pin, uint32_t val, uint32_t range) { (void) pin; (void) val; (void) range; return false; }
|
||||
|
||||
|
||||
// Timer is 80MHz fixed. 160MHz CPU frequency need scaling.
|
||||
constexpr bool ISCPUFREQ160MHZ = clockCyclesPerMicrosecond() == 160;
|
||||
// Maximum delay between IRQs, Timer1, <= 2^23 / 80MHz
|
||||
constexpr int32_t MAXIRQTICKSCCYS = microsecondsToClockCycles(10000);
|
||||
// Maximum servicing time for any single IRQ
|
||||
constexpr uint32_t ISRTIMEOUTCCYS = microsecondsToClockCycles(18);
|
||||
// The latency between in-ISR rearming of the timer and the earliest firing
|
||||
constexpr int32_t IRQLATENCYCCYS = microsecondsToClockCycles(2);
|
||||
// The SDK and hardware take some time to actually get to our NMI code
|
||||
constexpr int32_t DELTAIRQCCYS = ISCPUFREQ160MHZ ?
|
||||
microsecondsToClockCycles(2) >> 1 : microsecondsToClockCycles(2);
|
||||
|
||||
// for INFINITE, the NMI proceeds on the waveform without expiry deadline.
|
||||
// for EXPIRES, the NMI expires the waveform automatically on the expiry ccy.
|
||||
// for UPDATEEXPIRY, the NMI recomputes the exact expiry ccy and transitions to EXPIRES.
|
||||
// for UPDATEPHASE, the NMI recomputes the target timings
|
||||
// for INIT, the NMI initializes nextPeriodCcy, and if expiryCcy != 0 includes UPDATEEXPIRY.
|
||||
enum class WaveformMode : uint8_t {INFINITE = 0, EXPIRES = 1, UPDATEEXPIRY = 2, UPDATEPHASE = 3, INIT = 4};
|
||||
|
||||
// Waveform generator can create tones, PWM, and servos
|
||||
typedef struct {
|
||||
uint32_t nextPeriodCcy; // ESP clock cycle when a period begins.
|
||||
uint32_t endDutyCcy; // ESP clock cycle when going from duty to off
|
||||
int32_t dutyCcys; // Set next off cycle at low->high to maintain phase
|
||||
int32_t adjDutyCcys; // Temporary correction for next period
|
||||
int32_t periodCcys; // Set next phase cycle at low->high to maintain phase
|
||||
uint32_t expiryCcy; // For time-limited waveform, the CPU clock cycle when this waveform must stop. If WaveformMode::UPDATE, temporarily holds relative ccy count
|
||||
WaveformMode mode;
|
||||
bool autoPwm; // perform PWM duty to idle cycle ratio correction under high load at the expense of precise timings
|
||||
} Waveform;
|
||||
|
||||
namespace {
|
||||
|
||||
static struct {
|
||||
Waveform pins[17]; // State of all possible pins
|
||||
uint32_t states = 0; // Is the pin high or low, updated in NMI so no access outside the NMI code
|
||||
uint32_t enabled = 0; // Is it actively running, updated in NMI so no access outside the NMI code
|
||||
|
||||
// Enable lock-free by only allowing updates to waveform.states and waveform.enabled from IRQ service routine
|
||||
int32_t toSetBits = 0; // Message to the NMI handler to start/modify exactly one waveform
|
||||
int32_t toDisableBits = 0; // Message to the NMI handler to disable exactly one pin from waveform generation
|
||||
|
||||
// toSetBits temporaries
|
||||
// cheaper than packing them in every Waveform, since we permit only one use at a time
|
||||
uint32_t phaseCcy; // positive phase offset ccy count
|
||||
int8_t alignPhase; // < 0 no phase alignment, otherwise starts waveform in relative phase offset to given pin
|
||||
|
||||
uint32_t(*timer1CB)() = nullptr;
|
||||
|
||||
bool timer1Running = false;
|
||||
|
||||
uint32_t nextEventCcy;
|
||||
} waveform;
|
||||
|
||||
}
|
||||
|
||||
// Interrupt on/off control
|
||||
static IRAM_ATTR void timer1Interrupt();
|
||||
|
||||
// Non-speed critical bits
|
||||
#pragma GCC optimize ("Os")
|
||||
|
||||
static void initTimer() {
|
||||
timer1_disable();
|
||||
ETS_FRC_TIMER1_INTR_ATTACH(NULL, NULL);
|
||||
ETS_FRC_TIMER1_NMI_INTR_ATTACH(timer1Interrupt);
|
||||
timer1_enable(TIM_DIV1, TIM_EDGE, TIM_SINGLE);
|
||||
waveform.timer1Running = true;
|
||||
timer1_write(IRQLATENCYCCYS); // Cause an interrupt post-haste
|
||||
}
|
||||
|
||||
static void IRAM_ATTR deinitTimer() {
|
||||
ETS_FRC_TIMER1_NMI_INTR_ATTACH(NULL);
|
||||
timer1_disable();
|
||||
timer1_isr_init();
|
||||
waveform.timer1Running = false;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
// Set a callback. Pass in NULL to stop it
|
||||
void setTimer1Callback_weak(uint32_t (*fn)()) {
|
||||
waveform.timer1CB = fn;
|
||||
std::atomic_thread_fence(std::memory_order_acq_rel);
|
||||
if (!waveform.timer1Running && fn) {
|
||||
initTimer();
|
||||
} else if (waveform.timer1Running && !fn && !waveform.enabled) {
|
||||
deinitTimer();
|
||||
}
|
||||
}
|
||||
|
||||
// Start up a waveform on a pin, or change the current one. Will change to the new
|
||||
// waveform smoothly on next low->high transition. For immediate change, stopWaveform()
|
||||
// first, then it will immediately begin.
|
||||
int startWaveformClockCycles_weak(uint8_t pin, uint32_t highCcys, uint32_t lowCcys,
|
||||
uint32_t runTimeCcys, int8_t alignPhase, uint32_t phaseOffsetCcys, bool autoPwm) {
|
||||
uint32_t periodCcys = highCcys + lowCcys;
|
||||
if (periodCcys < MAXIRQTICKSCCYS) {
|
||||
if (!highCcys) {
|
||||
periodCcys = (MAXIRQTICKSCCYS / periodCcys) * periodCcys;
|
||||
}
|
||||
else if (!lowCcys) {
|
||||
highCcys = periodCcys = (MAXIRQTICKSCCYS / periodCcys) * periodCcys;
|
||||
}
|
||||
}
|
||||
// sanity checks, including mixed signed/unsigned arithmetic safety
|
||||
if ((pin > 16) || isFlashInterfacePin(pin) || (alignPhase > 16) ||
|
||||
static_cast<int32_t>(periodCcys) <= 0 ||
|
||||
static_cast<int32_t>(highCcys) < 0 || static_cast<int32_t>(lowCcys) < 0) {
|
||||
return false;
|
||||
}
|
||||
Waveform& wave = waveform.pins[pin];
|
||||
wave.dutyCcys = highCcys;
|
||||
wave.adjDutyCcys = 0;
|
||||
wave.periodCcys = periodCcys;
|
||||
wave.autoPwm = autoPwm;
|
||||
waveform.alignPhase = (alignPhase < 0) ? -1 : alignPhase;
|
||||
waveform.phaseCcy = phaseOffsetCcys;
|
||||
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
const uint32_t pinBit = 1UL << pin;
|
||||
if (!(waveform.enabled & pinBit)) {
|
||||
// wave.nextPeriodCcy and wave.endDutyCcy are initialized by the ISR
|
||||
wave.expiryCcy = runTimeCcys; // in WaveformMode::INIT, temporarily hold relative cycle count
|
||||
wave.mode = WaveformMode::INIT;
|
||||
if (!wave.dutyCcys) {
|
||||
// If initially at zero duty cycle, force GPIO off
|
||||
if (pin == 16) {
|
||||
GP16O = 0;
|
||||
}
|
||||
else {
|
||||
GPOC = pinBit;
|
||||
}
|
||||
}
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
waveform.toSetBits = 1UL << pin;
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
if (!waveform.timer1Running) {
|
||||
initTimer();
|
||||
}
|
||||
else if (T1V > IRQLATENCYCCYS) {
|
||||
// Must not interfere if Timer is due shortly
|
||||
timer1_write(IRQLATENCYCCYS);
|
||||
}
|
||||
}
|
||||
else {
|
||||
wave.mode = WaveformMode::INFINITE; // turn off possible expiry to make update atomic from NMI
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
if (runTimeCcys) {
|
||||
wave.expiryCcy = runTimeCcys; // in WaveformMode::UPDATEEXPIRY, temporarily hold relative cycle count
|
||||
wave.mode = WaveformMode::UPDATEEXPIRY;
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
waveform.toSetBits = 1UL << pin;
|
||||
} else if (alignPhase >= 0) {
|
||||
// @willmmiles new feature
|
||||
wave.mode = WaveformMode::UPDATEPHASE; // recalculate start
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
waveform.toSetBits = 1UL << pin;
|
||||
}
|
||||
}
|
||||
std::atomic_thread_fence(std::memory_order_acq_rel);
|
||||
while (waveform.toSetBits) {
|
||||
esp_yield(); // Wait for waveform to update
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Stops a waveform on a pin
|
||||
IRAM_ATTR int stopWaveform_weak(uint8_t pin) {
|
||||
// Can't possibly need to stop anything if there is no timer active
|
||||
if (!waveform.timer1Running) {
|
||||
return false;
|
||||
}
|
||||
// If user sends in a pin >16 but <32, this will always point to a 0 bit
|
||||
// If they send >=32, then the shift will result in 0 and it will also return false
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
const uint32_t pinBit = 1UL << pin;
|
||||
if (waveform.enabled & pinBit) {
|
||||
waveform.toDisableBits = 1UL << pin;
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
// Must not interfere if Timer is due shortly
|
||||
if (T1V > IRQLATENCYCCYS) {
|
||||
timer1_write(IRQLATENCYCCYS);
|
||||
}
|
||||
while (waveform.toDisableBits) {
|
||||
/* no-op */ // Can't delay() since stopWaveform may be called from an IRQ
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
}
|
||||
}
|
||||
if (!waveform.enabled && !waveform.timer1CB) {
|
||||
deinitTimer();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Speed critical bits
|
||||
#pragma GCC optimize ("O2")
|
||||
|
||||
// For dynamic CPU clock frequency switch in loop the scaling logic would have to be adapted.
|
||||
// Using constexpr makes sure that the CPU clock frequency is compile-time fixed.
|
||||
static inline IRAM_ATTR int32_t scaleCcys(const int32_t ccys, const bool isCPU2X) {
|
||||
if (ISCPUFREQ160MHZ) {
|
||||
return isCPU2X ? ccys : (ccys >> 1);
|
||||
}
|
||||
else {
|
||||
return isCPU2X ? (ccys << 1) : ccys;
|
||||
}
|
||||
}
|
||||
|
||||
static IRAM_ATTR void timer1Interrupt() {
|
||||
const uint32_t isrStartCcy = ESP.getCycleCount();
|
||||
//int32_t clockDrift = isrStartCcy - waveform.nextEventCcy;
|
||||
|
||||
// ----- @willmmiles begin patch -----
|
||||
nmiCrashWorkaround();
|
||||
// ----- @willmmiles end patch -----
|
||||
|
||||
const bool isCPU2X = CPU2X & 1;
|
||||
if ((waveform.toSetBits && !(waveform.enabled & waveform.toSetBits)) || waveform.toDisableBits) {
|
||||
// Handle enable/disable requests from main app.
|
||||
waveform.enabled = (waveform.enabled & ~waveform.toDisableBits) | waveform.toSetBits; // Set the requested waveforms on/off
|
||||
// Find the first GPIO being generated by checking GCC's find-first-set (returns 1 + the bit of the first 1 in an int32_t)
|
||||
waveform.toDisableBits = 0;
|
||||
}
|
||||
|
||||
if (waveform.toSetBits) {
|
||||
const int toSetPin = __builtin_ffs(waveform.toSetBits) - 1;
|
||||
Waveform& wave = waveform.pins[toSetPin];
|
||||
switch (wave.mode) {
|
||||
case WaveformMode::INIT:
|
||||
waveform.states &= ~waveform.toSetBits; // Clear the state of any just started
|
||||
if (waveform.alignPhase >= 0 && waveform.enabled & (1UL << waveform.alignPhase)) {
|
||||
wave.nextPeriodCcy = waveform.pins[waveform.alignPhase].nextPeriodCcy + scaleCcys(waveform.phaseCcy, isCPU2X);
|
||||
}
|
||||
else {
|
||||
wave.nextPeriodCcy = waveform.nextEventCcy;
|
||||
}
|
||||
if (!wave.expiryCcy) {
|
||||
wave.mode = WaveformMode::INFINITE;
|
||||
break;
|
||||
}
|
||||
// fall through
|
||||
case WaveformMode::UPDATEEXPIRY:
|
||||
// in WaveformMode::UPDATEEXPIRY, expiryCcy temporarily holds relative CPU cycle count
|
||||
wave.expiryCcy = wave.nextPeriodCcy + scaleCcys(wave.expiryCcy, isCPU2X);
|
||||
wave.mode = WaveformMode::EXPIRES;
|
||||
break;
|
||||
// @willmmiles new feature
|
||||
case WaveformMode::UPDATEPHASE:
|
||||
// in WaveformMode::UPDATEPHASE, we recalculate the targets
|
||||
if ((waveform.alignPhase >= 0) && (waveform.enabled & (1UL << waveform.alignPhase))) {
|
||||
// Compute phase shift to realign with target
|
||||
auto const newPeriodCcy = waveform.pins[waveform.alignPhase].nextPeriodCcy + scaleCcys(waveform.phaseCcy, isCPU2X);
|
||||
auto const period = scaleCcys(wave.periodCcys, isCPU2X);
|
||||
auto shift = ((static_cast<int32_t> (newPeriodCcy - wave.nextPeriodCcy) + period/2) % period) - (period/2);
|
||||
wave.nextPeriodCcy += static_cast<uint32_t>(shift);
|
||||
if (static_cast<int32_t>(wave.endDutyCcy - wave.nextPeriodCcy) > 0) {
|
||||
wave.endDutyCcy = wave.nextPeriodCcy;
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
waveform.toSetBits = 0;
|
||||
}
|
||||
|
||||
// Exit the loop if the next event, if any, is sufficiently distant.
|
||||
const uint32_t isrTimeoutCcy = isrStartCcy + ISRTIMEOUTCCYS;
|
||||
uint32_t busyPins = waveform.enabled;
|
||||
waveform.nextEventCcy = isrStartCcy + MAXIRQTICKSCCYS;
|
||||
|
||||
uint32_t now = ESP.getCycleCount();
|
||||
uint32_t isrNextEventCcy = now;
|
||||
while (busyPins) {
|
||||
if (static_cast<int32_t>(isrNextEventCcy - now) > IRQLATENCYCCYS) {
|
||||
waveform.nextEventCcy = isrNextEventCcy;
|
||||
break;
|
||||
}
|
||||
isrNextEventCcy = waveform.nextEventCcy;
|
||||
uint32_t loopPins = busyPins;
|
||||
while (loopPins) {
|
||||
const int pin = __builtin_ffsl(loopPins) - 1;
|
||||
const uint32_t pinBit = 1UL << pin;
|
||||
loopPins ^= pinBit;
|
||||
|
||||
Waveform& wave = waveform.pins[pin];
|
||||
|
||||
/* @willmmiles - wtf? We don't want to accumulate drift
|
||||
if (clockDrift) {
|
||||
wave.endDutyCcy += clockDrift;
|
||||
wave.nextPeriodCcy += clockDrift;
|
||||
wave.expiryCcy += clockDrift;
|
||||
}
|
||||
*/
|
||||
|
||||
uint32_t waveNextEventCcy = (waveform.states & pinBit) ? wave.endDutyCcy : wave.nextPeriodCcy;
|
||||
if (WaveformMode::EXPIRES == wave.mode &&
|
||||
static_cast<int32_t>(waveNextEventCcy - wave.expiryCcy) >= 0 &&
|
||||
static_cast<int32_t>(now - wave.expiryCcy) >= 0) {
|
||||
// Disable any waveforms that are done
|
||||
waveform.enabled ^= pinBit;
|
||||
busyPins ^= pinBit;
|
||||
}
|
||||
else {
|
||||
const int32_t overshootCcys = now - waveNextEventCcy;
|
||||
if (overshootCcys >= 0) {
|
||||
const int32_t periodCcys = scaleCcys(wave.periodCcys, isCPU2X);
|
||||
if (waveform.states & pinBit) {
|
||||
// active configuration and forward are 100% duty
|
||||
if (wave.periodCcys == wave.dutyCcys) {
|
||||
wave.nextPeriodCcy += periodCcys;
|
||||
wave.endDutyCcy = wave.nextPeriodCcy;
|
||||
}
|
||||
else {
|
||||
if (wave.autoPwm) {
|
||||
wave.adjDutyCcys += overshootCcys;
|
||||
}
|
||||
waveform.states ^= pinBit;
|
||||
if (16 == pin) {
|
||||
GP16O = 0;
|
||||
}
|
||||
else {
|
||||
GPOC = pinBit;
|
||||
}
|
||||
}
|
||||
waveNextEventCcy = wave.nextPeriodCcy;
|
||||
}
|
||||
else {
|
||||
wave.nextPeriodCcy += periodCcys;
|
||||
if (!wave.dutyCcys) {
|
||||
wave.endDutyCcy = wave.nextPeriodCcy;
|
||||
}
|
||||
else {
|
||||
int32_t dutyCcys = scaleCcys(wave.dutyCcys, isCPU2X);
|
||||
if (dutyCcys <= wave.adjDutyCcys) {
|
||||
dutyCcys >>= 1;
|
||||
wave.adjDutyCcys -= dutyCcys;
|
||||
}
|
||||
else if (wave.adjDutyCcys) {
|
||||
dutyCcys -= wave.adjDutyCcys;
|
||||
wave.adjDutyCcys = 0;
|
||||
}
|
||||
wave.endDutyCcy = now + dutyCcys;
|
||||
if (static_cast<int32_t>(wave.endDutyCcy - wave.nextPeriodCcy) > 0) {
|
||||
wave.endDutyCcy = wave.nextPeriodCcy;
|
||||
}
|
||||
waveform.states |= pinBit;
|
||||
if (16 == pin) {
|
||||
GP16O = 1;
|
||||
}
|
||||
else {
|
||||
GPOS = pinBit;
|
||||
}
|
||||
}
|
||||
waveNextEventCcy = wave.endDutyCcy;
|
||||
}
|
||||
|
||||
if (WaveformMode::EXPIRES == wave.mode && static_cast<int32_t>(waveNextEventCcy - wave.expiryCcy) > 0) {
|
||||
waveNextEventCcy = wave.expiryCcy;
|
||||
}
|
||||
}
|
||||
|
||||
if (static_cast<int32_t>(waveNextEventCcy - isrTimeoutCcy) >= 0) {
|
||||
busyPins ^= pinBit;
|
||||
if (static_cast<int32_t>(waveform.nextEventCcy - waveNextEventCcy) > 0) {
|
||||
waveform.nextEventCcy = waveNextEventCcy;
|
||||
}
|
||||
}
|
||||
else if (static_cast<int32_t>(isrNextEventCcy - waveNextEventCcy) > 0) {
|
||||
isrNextEventCcy = waveNextEventCcy;
|
||||
}
|
||||
}
|
||||
now = ESP.getCycleCount();
|
||||
}
|
||||
//clockDrift = 0;
|
||||
}
|
||||
|
||||
int32_t callbackCcys = 0;
|
||||
if (waveform.timer1CB) {
|
||||
callbackCcys = scaleCcys(waveform.timer1CB(), isCPU2X);
|
||||
}
|
||||
now = ESP.getCycleCount();
|
||||
int32_t nextEventCcys = waveform.nextEventCcy - now;
|
||||
// Account for unknown duration of timer1CB().
|
||||
if (waveform.timer1CB && nextEventCcys > callbackCcys) {
|
||||
waveform.nextEventCcy = now + callbackCcys;
|
||||
nextEventCcys = callbackCcys;
|
||||
}
|
||||
|
||||
// Timer is 80MHz fixed. 160MHz CPU frequency need scaling.
|
||||
int32_t deltaIrqCcys = DELTAIRQCCYS;
|
||||
int32_t irqLatencyCcys = IRQLATENCYCCYS;
|
||||
if (isCPU2X) {
|
||||
nextEventCcys >>= 1;
|
||||
deltaIrqCcys >>= 1;
|
||||
irqLatencyCcys >>= 1;
|
||||
}
|
||||
|
||||
// Firing timer too soon, the NMI occurs before ISR has returned.
|
||||
if (nextEventCcys < irqLatencyCcys + deltaIrqCcys) {
|
||||
waveform.nextEventCcy = now + IRQLATENCYCCYS + DELTAIRQCCYS;
|
||||
nextEventCcys = irqLatencyCcys;
|
||||
}
|
||||
else {
|
||||
nextEventCcys -= deltaIrqCcys;
|
||||
}
|
||||
|
||||
// Register access is fast and edge IRQ was configured before.
|
||||
T1L = nextEventCcys;
|
||||
}
|
788
package-lock.json
generated
Normal file
@ -0,0 +1,788 @@
|
||||
{
|
||||
"name": "wled",
|
||||
"version": "0.16.0-alpha",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "wled",
|
||||
"version": "0.16.0-alpha",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"clean-css": "^5.3.3",
|
||||
"html-minifier-terser": "^7.2.0",
|
||||
"nodemon": "^3.1.9",
|
||||
"web-resource-inliner": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
|
||||
"integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.2.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.24"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/set-array": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
|
||||
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/source-map": {
|
||||
"version": "0.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
|
||||
"integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.25"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
|
||||
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.25",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
|
||||
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.14.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
|
||||
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-colors": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
|
||||
"integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/anymatch": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"normalize-path": "^3.0.0",
|
||||
"picomatch": "^2.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/camel-case": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
|
||||
"integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pascal-case": "^3.1.2",
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"anymatch": "~3.1.2",
|
||||
"braces": "~3.0.2",
|
||||
"glob-parent": "~5.1.2",
|
||||
"is-binary-path": "~2.1.0",
|
||||
"is-glob": "~4.0.1",
|
||||
"normalize-path": "~3.0.0",
|
||||
"readdirp": "~3.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/clean-css": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
|
||||
"integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"source-map": "~0.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
|
||||
"integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/dom-serializer": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
|
||||
"integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.0.1",
|
||||
"domhandler": "^4.2.0",
|
||||
"entities": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/dom-serializer/node_modules/domhandler": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
|
||||
"integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/dom-serializer/node_modules/entities": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
|
||||
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
|
||||
"license": "BSD-2-Clause",
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/domelementtype": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
|
||||
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fb55"
|
||||
}
|
||||
],
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/domhandler": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz",
|
||||
"integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/domutils": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
|
||||
"integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"dom-serializer": "^1.0.1",
|
||||
"domelementtype": "^2.2.0",
|
||||
"domhandler": "^4.2.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/domutils/node_modules/domhandler": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
|
||||
"integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/dot-case": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
|
||||
"integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"no-case": "^3.0.4",
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-goat": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-3.0.0.tgz",
|
||||
"integrity": "sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/html-minifier-terser": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz",
|
||||
"integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"camel-case": "^4.1.2",
|
||||
"clean-css": "~5.3.2",
|
||||
"commander": "^10.0.0",
|
||||
"entities": "^4.4.0",
|
||||
"param-case": "^3.0.4",
|
||||
"relateurl": "^0.2.7",
|
||||
"terser": "^5.15.1"
|
||||
},
|
||||
"bin": {
|
||||
"html-minifier-terser": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.13.1 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/htmlparser2": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-5.0.1.tgz",
|
||||
"integrity": "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.0.1",
|
||||
"domhandler": "^3.3.0",
|
||||
"domutils": "^2.4.2",
|
||||
"entities": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/htmlparser2?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/htmlparser2/node_modules/entities": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
|
||||
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
|
||||
"license": "BSD-2-Clause",
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/ignore-by-default": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
|
||||
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"binary-extensions": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-glob": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-extglob": "^2.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-number": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lower-case": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
|
||||
"integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
|
||||
"integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/no-case": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
|
||||
"integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lower-case": "^2.0.2",
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon": {
|
||||
"version": "3.1.9",
|
||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz",
|
||||
"integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chokidar": "^3.5.2",
|
||||
"debug": "^4",
|
||||
"ignore-by-default": "^1.0.1",
|
||||
"minimatch": "^3.1.2",
|
||||
"pstree.remy": "^1.1.8",
|
||||
"semver": "^7.5.3",
|
||||
"simple-update-notifier": "^2.0.0",
|
||||
"supports-color": "^5.5.0",
|
||||
"touch": "^3.1.0",
|
||||
"undefsafe": "^2.0.5"
|
||||
},
|
||||
"bin": {
|
||||
"nodemon": "bin/nodemon.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/nodemon"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/param-case": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
|
||||
"integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dot-case": "^3.0.4",
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/pascal-case": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
|
||||
"integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"no-case": "^3.0.4",
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/pstree.remy": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
|
||||
"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"picomatch": "^2.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/relateurl": {
|
||||
"version": "0.2.7",
|
||||
"resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
|
||||
"integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz",
|
||||
"integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-update-notifier": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
|
||||
"integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"semver": "^7.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-support": {
|
||||
"version": "0.5.21",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
|
||||
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-flag": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.37.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz",
|
||||
"integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.3",
|
||||
"acorn": "^8.8.2",
|
||||
"commander": "^2.20.0",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
"bin": {
|
||||
"terser": "bin/terser"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/terser/node_modules/commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/touch": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz",
|
||||
"integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"nodetouch": "bin/nodetouch.js"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/undefsafe": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
|
||||
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/valid-data-url": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-3.0.1.tgz",
|
||||
"integrity": "sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/web-resource-inliner": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/web-resource-inliner/-/web-resource-inliner-7.0.0.tgz",
|
||||
"integrity": "sha512-NlfnGF8MY9ZUwFjyq3vOUBx7KwF8bmE+ywR781SB0nWB6MoMxN4BA8gtgP1KGTZo/O/AyWJz7HZpR704eaj4mg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-colors": "^4.1.1",
|
||||
"escape-goat": "^3.0.0",
|
||||
"htmlparser2": "^5.0.0",
|
||||
"mime": "^2.4.6",
|
||||
"valid-data-url": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
34
package.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "wled",
|
||||
"version": "0.16.0-alpha",
|
||||
"description": "Tools for WLED project",
|
||||
"main": "tools/cdata.js",
|
||||
"directories": {
|
||||
"lib": "lib",
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "node tools/cdata.js",
|
||||
"test": "node --test",
|
||||
"dev": "nodemon -e js,html,htm,css,png,jpg,gif,ico,js -w tools/ -w wled00/data/ -x node tools/cdata.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/wled-dev/WLED.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/wled-dev/WLED/issues"
|
||||
},
|
||||
"homepage": "https://github.com/wled-dev/WLED#readme",
|
||||
"dependencies": {
|
||||
"clean-css": "^5.3.3",
|
||||
"html-minifier-terser": "^7.2.0",
|
||||
"web-resource-inliner": "^7.0.0",
|
||||
"nodemon": "^3.1.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
}
|
21
pio-scripts/build_ui.py
Normal file
@ -0,0 +1,21 @@
|
||||
Import("env")
|
||||
import shutil
|
||||
|
||||
node_ex = shutil.which("node")
|
||||
# Check if Node.js is installed and present in PATH if it failed, abort the build
|
||||
if node_ex is None:
|
||||
print('\x1b[0;31;43m' + 'Node.js is not installed or missing from PATH html css js will not be processed check https://kno.wled.ge/advanced/compiling-wled/' + '\x1b[0m')
|
||||
exitCode = env.Execute("null")
|
||||
exit(exitCode)
|
||||
else:
|
||||
# Install the necessary node packages for the pre-build asset bundling script
|
||||
print('\x1b[6;33;42m' + 'Installing node packages' + '\x1b[0m')
|
||||
env.Execute("npm ci")
|
||||
|
||||
# Call the bundling script
|
||||
exitCode = env.Execute("npm run build")
|
||||
|
||||
# If it failed, abort the build
|
||||
if (exitCode):
|
||||
print('\x1b[0;31;43m' + 'npm run build fails check https://kno.wled.ge/advanced/compiling-wled/' + '\x1b[0m')
|
||||
exit(exitCode)
|
115
pio-scripts/load_usermods.py
Normal file
@ -0,0 +1,115 @@
|
||||
Import('env')
|
||||
import os.path
|
||||
from collections import deque
|
||||
from pathlib import Path # For OS-agnostic path manipulation
|
||||
from platformio.package.manager.library import LibraryPackageManager
|
||||
|
||||
usermod_dir = Path(env["PROJECT_DIR"]) / "usermods"
|
||||
all_usermods = [f for f in usermod_dir.iterdir() if f.is_dir() and f.joinpath('library.json').exists()]
|
||||
|
||||
if env['PIOENV'] == "usermods":
|
||||
# Add all usermods
|
||||
env.GetProjectConfig().set(f"env:usermods", 'custom_usermods', " ".join([f.name for f in all_usermods]))
|
||||
|
||||
def find_usermod(mod: str):
|
||||
"""Locate this library in the usermods folder.
|
||||
We do this to avoid needing to rename a bunch of folders;
|
||||
this could be removed later
|
||||
"""
|
||||
# Check name match
|
||||
mp = usermod_dir / mod
|
||||
if mp.exists():
|
||||
return mp
|
||||
mp = usermod_dir / f"{mod}_v2"
|
||||
if mp.exists():
|
||||
return mp
|
||||
mp = usermod_dir / f"usermod_v2_{mod}"
|
||||
if mp.exists():
|
||||
return mp
|
||||
raise RuntimeError(f"Couldn't locate module {mod} in usermods directory!")
|
||||
|
||||
usermods = env.GetProjectOption("custom_usermods","")
|
||||
if usermods:
|
||||
# Inject usermods in to project lib_deps
|
||||
proj = env.GetProjectConfig()
|
||||
deps = env.GetProjectOption('lib_deps')
|
||||
src_dir = proj.get("platformio", "src_dir")
|
||||
src_dir = src_dir.replace('\\','/')
|
||||
mod_paths = {mod: find_usermod(mod) for mod in usermods.split()}
|
||||
usermods = [f"{mod} = symlink://{path}" for mod, path in mod_paths.items()]
|
||||
proj.set("env:" + env['PIOENV'], 'lib_deps', deps + usermods)
|
||||
# Force usermods to be installed in to the environment build state before the LDF runs
|
||||
# Otherwise we won't be able to see them until it's too late to change their paths for LDF
|
||||
# Logic is largely borrowed from PlaformIO internals
|
||||
not_found_specs = []
|
||||
for spec in usermods:
|
||||
found = False
|
||||
for storage_dir in env.GetLibSourceDirs():
|
||||
#print(f"Checking {storage_dir} for {spec}")
|
||||
lm = LibraryPackageManager(storage_dir)
|
||||
if lm.get_package(spec):
|
||||
#print("Found!")
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
#print("Missing!")
|
||||
not_found_specs.append(spec)
|
||||
if not_found_specs:
|
||||
lm = LibraryPackageManager(
|
||||
env.subst(os.path.join("$PROJECT_LIBDEPS_DIR", "$PIOENV"))
|
||||
)
|
||||
for spec in not_found_specs:
|
||||
#print(f"LU: forcing install of {spec}")
|
||||
lm.install(spec)
|
||||
|
||||
|
||||
# Utility function for assembling usermod include paths
|
||||
def cached_add_includes(dep, dep_cache: set, includes: deque):
|
||||
""" Add dep's include paths to includes if it's not in the cache """
|
||||
if dep not in dep_cache:
|
||||
dep_cache.add(dep)
|
||||
for include in dep.get_include_dirs():
|
||||
if include not in includes:
|
||||
includes.appendleft(include)
|
||||
if usermod_dir not in Path(dep.src_dir).parents:
|
||||
# Recurse, but only for NON-usermods
|
||||
for subdep in dep.depbuilders:
|
||||
cached_add_includes(subdep, dep_cache, includes)
|
||||
|
||||
# Monkey-patch ConfigureProjectLibBuilder to mark up the dependencies
|
||||
# Save the old value
|
||||
old_ConfigureProjectLibBuilder = env.ConfigureProjectLibBuilder
|
||||
|
||||
# Our new wrapper
|
||||
def wrapped_ConfigureProjectLibBuilder(xenv):
|
||||
# Update usermod properties
|
||||
# Set libArchive before build actions are added
|
||||
for um in (um for um in xenv.GetLibBuilders() if usermod_dir in Path(um.src_dir).parents):
|
||||
build = um._manifest.get("build", {})
|
||||
build["libArchive"] = False
|
||||
um._manifest["build"] = build
|
||||
|
||||
# Call the wrapped function
|
||||
result = old_ConfigureProjectLibBuilder.clone(xenv)()
|
||||
|
||||
# Fix up include paths
|
||||
# In PlatformIO >=6.1.17, this could be done prior to ConfigureProjectLibBuilder
|
||||
wled_dir = xenv["PROJECT_SRC_DIR"]
|
||||
# Build a list of dependency include dirs
|
||||
# TODO: Find out if this is the order that PlatformIO/SCons puts them in??
|
||||
processed_deps = set()
|
||||
extra_include_dirs = deque() # Deque used for fast prepend
|
||||
for dep in result.depbuilders:
|
||||
cached_add_includes(dep, processed_deps, extra_include_dirs)
|
||||
|
||||
for um in [dep for dep in result.depbuilders if usermod_dir in Path(dep.src_dir).parents]:
|
||||
# Add the wled folder to the include path
|
||||
um.env.PrependUnique(CPPPATH=wled_dir)
|
||||
# Add WLED's own dependencies
|
||||
for dir in extra_include_dirs:
|
||||
um.env.PrependUnique(CPPPATH=dir)
|
||||
|
||||
return result
|
||||
|
||||
# Apply the wrapper
|
||||
env.AddMethod(wrapped_ConfigureProjectLibBuilder, "ConfigureProjectLibBuilder")
|
24
pio-scripts/obj-dump.py
Normal file
@ -0,0 +1,24 @@
|
||||
# Little convenience script to get an object dump
|
||||
# You may add "-S" to the objdump commandline (i.e. replace "-D -C " with "-d -S -C ")
|
||||
# to get source code intermixed with disassembly (SLOW !)
|
||||
|
||||
Import('env')
|
||||
|
||||
def obj_dump_after_elf(source, target, env):
|
||||
platform = env.PioPlatform()
|
||||
board = env.BoardConfig()
|
||||
mcu = board.get("build.mcu", "esp32")
|
||||
|
||||
print("Create firmware.asm")
|
||||
if mcu == "esp8266":
|
||||
env.Execute("xtensa-lx106-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
|
||||
if mcu == "esp32":
|
||||
env.Execute("xtensa-esp32-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
|
||||
if mcu == "esp32s2":
|
||||
env.Execute("xtensa-esp32s2-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
|
||||
if mcu == "esp32s3":
|
||||
env.Execute("xtensa-esp32s3-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
|
||||
if mcu == "esp32c3":
|
||||
env.Execute("riscv32-esp-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
|
||||
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf", [obj_dump_after_elf])
|
66
pio-scripts/output_bins.py
Normal file
@ -0,0 +1,66 @@
|
||||
Import('env')
|
||||
import os
|
||||
import shutil
|
||||
import gzip
|
||||
|
||||
OUTPUT_DIR = "build_output{}".format(os.path.sep)
|
||||
#OUTPUT_DIR = os.path.join("build_output")
|
||||
|
||||
def _get_cpp_define_value(env, define):
|
||||
define_list = [item[-1] for item in env["CPPDEFINES"] if item[0] == define]
|
||||
|
||||
if define_list:
|
||||
return define_list[0]
|
||||
|
||||
return None
|
||||
|
||||
def _create_dirs(dirs=["map", "release", "firmware"]):
|
||||
for d in dirs:
|
||||
os.makedirs(os.path.join(OUTPUT_DIR, d), exist_ok=True)
|
||||
|
||||
def create_release(source):
|
||||
release_name_def = _get_cpp_define_value(env, "WLED_RELEASE_NAME")
|
||||
if release_name_def:
|
||||
release_name = release_name_def.replace("\\\"", "")
|
||||
version = _get_cpp_define_value(env, "WLED_VERSION")
|
||||
release_file = os.path.join(OUTPUT_DIR, "release", f"WLED_{version}_{release_name}.bin")
|
||||
release_gz_file = release_file + ".gz"
|
||||
print(f"Copying {source} to {release_file}")
|
||||
shutil.copy(source, release_file)
|
||||
bin_gzip(release_file, release_gz_file)
|
||||
else:
|
||||
variant = env["PIOENV"]
|
||||
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
|
||||
print(f"Copying {source} to {bin_file}")
|
||||
shutil.copy(source, bin_file)
|
||||
|
||||
def bin_rename_copy(source, target, env):
|
||||
_create_dirs()
|
||||
variant = env["PIOENV"]
|
||||
builddir = os.path.join(env["PROJECT_BUILD_DIR"], variant)
|
||||
source_map = os.path.join(builddir, env["PROGNAME"] + ".map")
|
||||
|
||||
# create string with location and file names based on variant
|
||||
map_file = "{}map{}{}.map".format(OUTPUT_DIR, os.path.sep, variant)
|
||||
|
||||
create_release(str(target[0]))
|
||||
|
||||
# copy firmware.map to map/<variant>.map
|
||||
if os.path.isfile("firmware.map"):
|
||||
print("Found linker mapfile firmware.map")
|
||||
shutil.copy("firmware.map", map_file)
|
||||
if os.path.isfile(source_map):
|
||||
print(f"Found linker mapfile {source_map}")
|
||||
shutil.copy(source_map, map_file)
|
||||
|
||||
def bin_gzip(source, target):
|
||||
# only create gzip for esp8266
|
||||
if not env["PIOPLATFORM"] == "espressif8266":
|
||||
return
|
||||
|
||||
print(f"Creating gzip file {target} from {source}")
|
||||
with open(source,"rb") as fp:
|
||||
with gzip.open(target, "wb", compresslevel = 9) as f:
|
||||
shutil.copyfileobj(fp, f)
|
||||
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", bin_rename_copy)
|
8
pio-scripts/set_version.py
Normal file
@ -0,0 +1,8 @@
|
||||
Import('env')
|
||||
import json
|
||||
|
||||
PACKAGE_FILE = "package.json"
|
||||
|
||||
with open(PACKAGE_FILE, "r") as package:
|
||||
version = json.load(package)["version"]
|
||||
env.Append(BUILD_FLAGS=[f"-DWLED_VERSION={version}"])
|
15
pio-scripts/strip-floats.py
Normal file
@ -0,0 +1,15 @@
|
||||
Import('env')
|
||||
|
||||
#
|
||||
# Dump build environment (for debug)
|
||||
#print env.Dump()
|
||||
#
|
||||
|
||||
flags = " ".join(env['LINKFLAGS'])
|
||||
flags = flags.replace("-u _printf_float", "")
|
||||
flags = flags.replace("-u _scanf_float", "")
|
||||
newflags = flags.split()
|
||||
|
||||
env.Replace(
|
||||
LINKFLAGS=newflags
|
||||
)
|
9
pio-scripts/user_config_copy.py
Normal file
@ -0,0 +1,9 @@
|
||||
Import('env')
|
||||
import os
|
||||
import shutil
|
||||
|
||||
# copy WLED00/my_config_sample.h to WLED00/my_config.h
|
||||
if os.path.isfile("wled00/my_config.h"):
|
||||
print ("*** use existing my_config.h ***")
|
||||
else:
|
||||
shutil.copy("wled00/my_config_sample.h", "wled00/my_config.h")
|
780
platformio.ini
@ -2,97 +2,77 @@
|
||||
; Please visit documentation: https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[platformio]
|
||||
src_dir = ./wled00
|
||||
data_dir = ./wled00/data
|
||||
lib_dir = ./wled00/src
|
||||
build_cache_dir = ~/.buildcache
|
||||
extra_configs =
|
||||
platformio_override.ini
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# ENVIRONMENTS
|
||||
#
|
||||
# Please uncomment one of the lines below to select your board(s)
|
||||
# (use `platformio_override.ini` when building for your own board; see `platformio_override.ini.sample` for an example)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Travis CI binaries (comment this out when building for single board)
|
||||
default_envs = d1_mini, esp01, esp01_1m_ota, esp32dev
|
||||
# CI/release binaries
|
||||
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, nodemcuv2_160, esp8266_2m_160, esp01_1m_full_160, nodemcuv2_compat, esp8266_2m_compat, esp01_1m_full_compat, esp32dev, esp32dev_V4, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_16MB_opi, esp32s3dev_8MB_opi, esp32s3_4M_qspi, esp32_wrover, usermods
|
||||
|
||||
# Release binaries
|
||||
; default_envs = nodemcuv2, esp01, esp01_1m_ota, esp01_1m_full, esp32dev, custom_WS2801, custom_APA102, custom_LEDPIN_16, custom_LEDPIN_4, custom32_LEDPIN_16
|
||||
|
||||
# Single binaries (uncomment your board)
|
||||
; default_envs = nodemcuv2
|
||||
; default_envs = esp01
|
||||
; default_envs = esp01_1m_ota
|
||||
; default_envs = esp01_1m_full
|
||||
; default_envs = esp07
|
||||
; default_envs = d1_mini
|
||||
; default_envs = heltec_wifi_kit_8
|
||||
; default_envs = h803wf
|
||||
; default_envs = d1_mini_debug
|
||||
; default_envs = d1_mini_ota
|
||||
; default_envs = esp32dev
|
||||
; default_envs = esp8285_4CH_MagicHome
|
||||
; default_envs = esp8285_4CH_H801
|
||||
; default_envs = esp8285_5CH_H801
|
||||
; default_envs = d1_mini_5CH_Shojo_PCB
|
||||
; default_envs = wemos_shield_esp32
|
||||
src_dir = ./wled00
|
||||
data_dir = ./wled00/data
|
||||
build_cache_dir = ~/.buildcache
|
||||
extra_configs =
|
||||
platformio_override.ini
|
||||
|
||||
[common]
|
||||
# ------------------------------------------------------------------------------
|
||||
# PLATFORM:
|
||||
# !! DO NOT confuse platformio's ESP8266 development platform with Arduino core for ESP8266
|
||||
#
|
||||
# arduino core 2.3.0 = platformIO 1.5.0
|
||||
# arduino core 2.4.0 = platformIO 1.6.0
|
||||
# arduino core 2.4.1 = platformIO 1.7.3
|
||||
# arduino core 2.4.2 = platformIO 1.8.0
|
||||
# arduino core 2.5.0 = platformIO 2.0.4
|
||||
# arduino core 2.5.1 = platformIO 2.1.1
|
||||
# arduino core 2.5.2 = platformIO 2.2.3
|
||||
# arduino core 2.6.1 = platformIO 2.3.0
|
||||
# arduino core 2.6.2 = platformIO 2.3.1
|
||||
# arduino core 2.6.3 = platformIO 2.3.2
|
||||
# arduino core 2.7.0 = platformIO 2.5.0
|
||||
# ------------------------------------------------------------------------------
|
||||
arduino_core_2_3_0 = espressif8266@1.5.0
|
||||
arduino_core_2_4_0 = espressif8266@1.6.0
|
||||
arduino_core_2_4_1 = espressif8266@1.7.3
|
||||
arduino_core_2_4_2 = espressif8266@1.8.0
|
||||
arduino_core_2_5_0 = espressif8266@2.0.4
|
||||
arduino_core_2_5_1 = espressif8266@2.1.1
|
||||
arduino_core_2_5_2 = espressif8266@2.2.3
|
||||
arduino_core_2_6_1 = espressif8266@2.3.0
|
||||
arduino_core_2_6_2 = espressif8266@2.3.1
|
||||
arduino_core_2_6_3 = espressif8266@2.3.3
|
||||
arduino_core_2_7_4 = espressif8266@2.6.2
|
||||
arduino_core_3_0_0 = espressif8266@3.0.0
|
||||
arduino_core_3_0_2 = espressif8266@3.2.0
|
||||
arduino_core_3_1_0 = espressif8266@4.1.0
|
||||
arduino_core_3_1_2 = espressif8266@4.2.1
|
||||
|
||||
# Development platforms
|
||||
arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop
|
||||
arduino_core_git = https://github.com/platformio/platform-espressif8266#feature/stage
|
||||
|
||||
platform = ${common.arduino_core_2_4_2}
|
||||
platform_latest = ${common.arduino_core_2_6_3}
|
||||
# Platform to use for ESP8266
|
||||
platform_wled_default = ${common.arduino_core_3_1_2}
|
||||
# We use 2.7.4.7 for all, includes PWM flicker fix and Wstring optimization
|
||||
#platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7
|
||||
platform_packages = platformio/toolchain-xtensa @ ~2.100300.220621 #2.40802.200502
|
||||
platformio/tool-esptool #@ ~1.413.0
|
||||
platformio/tool-esptoolpy #@ ~1.30000.0
|
||||
|
||||
## previous platform for 8266, in case of problems with the new one
|
||||
## you'll need makuna/NeoPixelBus@ 2.6.9 for arduino_core_3_0_2, which does not support Ucs890x
|
||||
;; platform_wled_default = ${common.arduino_core_3_0_2}
|
||||
;; platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7
|
||||
;; platformio/toolchain-xtensa @ ~2.40802.200502
|
||||
;; platformio/tool-esptool @ ~1.413.0
|
||||
;; platformio/tool-esptoolpy @ ~1.30000.0
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# FLAGS: DEBUG
|
||||
#
|
||||
# esp8266 : see https://docs.platformio.org/en/latest/platforms/espressif8266.html#debug-level
|
||||
# esp32 : see https://docs.platformio.org/en/latest/platforms/espressif32.html#debug-level
|
||||
# ------------------------------------------------------------------------------
|
||||
debug_flags = -D DEBUG=1 -D WLED_DEBUG -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_TLS_MEM
|
||||
#if needed (for memleaks etc) also add; -DDEBUG_ESP_OOM -include "umm_malloc/umm_malloc_cfg.h"
|
||||
#-DDEBUG_ESP_CORE is not working right now
|
||||
debug_flags = -D DEBUG=1 -D WLED_DEBUG
|
||||
-DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_TLS_MEM ;; for esp8266
|
||||
# if needed (for memleaks etc) also add; -DDEBUG_ESP_OOM -include "umm_malloc/umm_malloc_cfg.h"
|
||||
# -DDEBUG_ESP_CORE is not working right now
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# FLAGS: ldscript
|
||||
# ldscript_512k ( 512 KB) = 487 KB sketch, 4 KB eeprom, no spiffs, 16 KB reserved
|
||||
# ldscript_1m0m (1024 KB) = 999 KB sketch, 4 KB eeprom, no spiffs, 16 KB reserved
|
||||
# FLAGS: ldscript (available ldscripts at https://github.com/esp8266/Arduino/tree/master/tools/sdk/ld)
|
||||
# ldscript_2m1m (2048 KB) = 1019 KB sketch, 4 KB eeprom, 1004 KB spiffs, 16 KB reserved
|
||||
# ldscript_4m1m (4096 KB) = 1019 KB sketch, 4 KB eeprom, 1002 KB spiffs, 16 KB reserved, 2048 KB empty/ota?
|
||||
# ldscript_4m3m (4096 KB) = 1019 KB sketch, 4 KB eeprom, 3040 KB spiffs, 16 KB reserved
|
||||
#
|
||||
# Available lwIP variants (macros):
|
||||
# -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH = v1.4 Higher Bandwidth (default)
|
||||
# -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY = v2 Lower Memory
|
||||
# -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH = v2 Higher Bandwidth
|
||||
# -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
|
||||
#
|
||||
# BearSSL performance:
|
||||
# When building with -DSECURE_CLIENT=SECURE_CLIENT_BEARSSL, please add `board_build.f_cpu = 160000000` to the environment configuration
|
||||
@ -105,26 +85,38 @@ debug_flags = -D DEBUG=1 -D WLED_DEBUG -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT
|
||||
# TLS_RSA_WITH_AES_256_CBC_SHA / AES256-SHA
|
||||
# This reduces the OTA size with ~45KB, so it's especially useful on low memory boards (512k/1m).
|
||||
# ------------------------------------------------------------------------------
|
||||
build_flags = -g -w -DMQTT_MAX_PACKET_SIZE=1024 -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH
|
||||
-DSECURE_CLIENT=SECURE_CLIENT_BEARSSL -DBEARSSL_SSL_BASIC
|
||||
build_flags =
|
||||
-DMQTT_MAX_PACKET_SIZE=1024
|
||||
-DSECURE_CLIENT=SECURE_CLIENT_BEARSSL
|
||||
-DBEARSSL_SSL_BASIC
|
||||
-D CORE_DEBUG_LEVEL=0
|
||||
-D NDEBUG
|
||||
-Wno-attributes ;; silence warnings about unknown attribute 'maybe_unused' in NeoPixelBus
|
||||
#build_flags for the IRremoteESP8266 library (enabled decoders have to appear here)
|
||||
-D _IR_ENABLE_DEFAULT_=false
|
||||
-D DECODE_HASH=true
|
||||
-D _IR_ENABLE_DEFAULT_=false
|
||||
-D DECODE_HASH=true
|
||||
-D DECODE_NEC=true
|
||||
-D DECODE_SONY=true
|
||||
-D DECODE_SONY=true
|
||||
-D DECODE_SAMSUNG=true
|
||||
-D DECODE_LG=true
|
||||
|
||||
build_flags_esp8266 = ${common.build_flags} -DESP8266
|
||||
build_flags_esp32 = ${common.build_flags} -DARDUINO_ARCH_ESP32
|
||||
-DWLED_USE_MY_CONFIG
|
||||
|
||||
ldscript_512k = eagle.flash.512k.ld ;for older versions change this to eagle.flash.512k0.ld
|
||||
ldscript_1m0m = eagle.flash.1m.ld ;for older versions change this to eagle.flash.1m0.ld
|
||||
build_unflags =
|
||||
|
||||
ldscript_1m128k = eagle.flash.1m128.ld
|
||||
ldscript_2m512k = eagle.flash.2m512.ld
|
||||
ldscript_2m1m = eagle.flash.2m1m.ld
|
||||
ldscript_4m1m = eagle.flash.4m1m.ld
|
||||
ldscript_4m3m = eagle.flash.4m3m.ld
|
||||
|
||||
shared_libdeps_dir = ./wled00/src
|
||||
[scripts_defaults]
|
||||
extra_scripts =
|
||||
pre:pio-scripts/set_version.py
|
||||
post:pio-scripts/output_bins.py
|
||||
post:pio-scripts/strip-floats.py
|
||||
pre:pio-scripts/user_config_copy.py
|
||||
pre:pio-scripts/load_usermods.py
|
||||
pre:pio-scripts/build_ui.py
|
||||
; post:pio-scripts/obj-dump.py ;; convenience script to create a disassembly dump of the firmware (hardcore debugging)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# COMMON SETTINGS:
|
||||
@ -133,34 +125,229 @@ shared_libdeps_dir = ./wled00/src
|
||||
framework = arduino
|
||||
board_build.flash_mode = dout
|
||||
monitor_speed = 115200
|
||||
# slow upload speed but most compatible (use platformio_override.ini to use faster speed)
|
||||
upload_speed = 115200
|
||||
lib_extra_dirs =
|
||||
${common.shared_libdeps_dir}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# LIBRARIES: required dependencies
|
||||
# Please note that we don't always use the latest version of a library.
|
||||
#
|
||||
# The following libraries have been included (and some of them changd) in the source:
|
||||
# ArduinoJson@5.13.5, Blynk@0.5.4(changed), E131@1.0.0(changed), Time@1.5, Timezone@1.2.1
|
||||
# The following libraries have been included (and some of them changed) in the source:
|
||||
# ArduinoJson@5.13.5, E131@1.0.0(changed), Time@1.5, Timezone@1.2.1
|
||||
# ------------------------------------------------------------------------------
|
||||
lib_compat_mode = strict
|
||||
lib_deps =
|
||||
FastLED@3.3.2
|
||||
NeoPixelBus@2.5.7
|
||||
ESPAsyncTCP@1.2.0
|
||||
ESPAsyncUDP@697c75a025
|
||||
AsyncTCP@1.0.3
|
||||
Esp Async WebServer@1.2.0
|
||||
IRremoteESP8266@2.7.3
|
||||
#For use SSD1306 OLED display uncomment following
|
||||
#U8g2@~2.27.2
|
||||
#For Dallas sensor uncomment following 2 lines
|
||||
#OneWire@~2.3.5
|
||||
fastled/FastLED @ 3.6.0
|
||||
IRremoteESP8266 @ 2.8.2
|
||||
makuna/NeoPixelBus @ 2.8.3
|
||||
#https://github.com/makuna/NeoPixelBus.git#CoreShaderBeta
|
||||
https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.4.0
|
||||
# for I2C interface
|
||||
;Wire
|
||||
# ESP-NOW library
|
||||
;gmag11/QuickESPNow @ ~0.7.0
|
||||
https://github.com/blazoncek/QuickESPNow.git#optional-debug
|
||||
#For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line
|
||||
#TFT_eSPI
|
||||
#For compatible OLED display uncomment following
|
||||
#olikraus/U8g2 #@ ~2.33.15
|
||||
#For Dallas sensor uncomment following
|
||||
#paulstoffregen/OneWire @ ~2.3.8
|
||||
#For BME280 sensor uncomment following
|
||||
#BME280@~3.0.0
|
||||
lib_ignore =
|
||||
AsyncTCP
|
||||
#BME280 @ ~3.0.0
|
||||
;adafruit/Adafruit BMP280 Library @ 2.1.0
|
||||
;adafruit/Adafruit CCS811 Library @ 1.0.4
|
||||
;adafruit/Adafruit Si7021 Library @ 1.4.0
|
||||
#For MAX1704x Lipo Monitor / Fuel Gauge uncomment following
|
||||
; https://github.com/adafruit/Adafruit_BusIO @ 1.14.5
|
||||
; https://github.com/adafruit/Adafruit_MAX1704X @ 1.0.2
|
||||
#For MPU6050 IMU uncomment follwoing
|
||||
;electroniccats/MPU6050 @1.0.1
|
||||
# SHT85
|
||||
;robtillaart/SHT85@~0.3.3
|
||||
|
||||
extra_scripts = ${scripts_defaults.extra_scripts}
|
||||
|
||||
[esp8266]
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags =
|
||||
-DESP8266
|
||||
-DFP_IN_IROM
|
||||
;-Wno-deprecated-declarations
|
||||
;-Wno-register ;; leaves some warnings when compiling C files: command-line option '-Wno-register' is valid for C++/ObjC++ but not for C
|
||||
;-Dregister= # remove warnings in C++17 due to use of deprecated register keyword by the FastLED library ;; warning: this can be dangerous
|
||||
-Wno-misleading-indentation
|
||||
; NONOSDK22x_190703 = 2.2.2-dev(38a443e)
|
||||
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
|
||||
; lwIP 2 - Higher Bandwidth no Features
|
||||
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
|
||||
; lwIP 1.4 - Higher Bandwidth (Aircoookie has)
|
||||
-DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH
|
||||
; VTABLES in Flash
|
||||
-DVTABLES_IN_FLASH
|
||||
; restrict to minimal mime-types
|
||||
-DMIMETYPE_MINIMAL
|
||||
; other special-purpose framework flags (see https://docs.platformio.org/en/latest/platforms/espressif8266.html)
|
||||
; decrease code cache size and increase IRAM to fit all pixel functions
|
||||
-D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48 ;; in case of linker errors like "section `.text1' will not fit in region `iram1_0_seg'"
|
||||
; -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED ;; (experimental) adds some extra heap, but may cause slowdown
|
||||
-D NON32XFER_HANDLER ;; ask forgiveness for PROGMEM misuse
|
||||
|
||||
lib_deps =
|
||||
#https://github.com/lorol/LITTLEFS.git
|
||||
ESPAsyncTCP @ 1.2.2
|
||||
ESPAsyncUDP
|
||||
ESP8266PWM
|
||||
${env.lib_deps}
|
||||
|
||||
;; compatibilty flags - same as 0.14.0 which seems to work better on some 8266 boards. Not using PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48
|
||||
build_flags_compat =
|
||||
-DESP8266
|
||||
-DFP_IN_IROM
|
||||
;;-Wno-deprecated-declarations
|
||||
-Wno-misleading-indentation
|
||||
;;-Wno-attributes ;; silence warnings about unknown attribute 'maybe_unused' in NeoPixelBus
|
||||
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
|
||||
-DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH
|
||||
-DVTABLES_IN_FLASH
|
||||
-DMIMETYPE_MINIMAL
|
||||
-DWLED_SAVE_IRAM ;; needed to prevent linker error
|
||||
|
||||
;; this platform version was used for WLED 0.14.0
|
||||
platform_compat = espressif8266@4.2.0
|
||||
platform_packages_compat =
|
||||
platformio/toolchain-xtensa @ ~2.100300.220621 #2.40802.200502
|
||||
platformio/tool-esptool #@ ~1.413.0
|
||||
platformio/tool-esptoolpy #@ ~1.30000.0
|
||||
|
||||
;; experimental - for using older NeoPixelBus 2.7.9
|
||||
lib_deps_compat =
|
||||
ESPAsyncTCP @ 1.2.2
|
||||
ESPAsyncUDP
|
||||
ESP8266PWM
|
||||
fastled/FastLED @ 3.6.0
|
||||
IRremoteESP8266 @ 2.8.2
|
||||
makuna/NeoPixelBus @ 2.7.9
|
||||
https://github.com/blazoncek/QuickESPNow.git#optional-debug
|
||||
https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.4.0
|
||||
|
||||
[esp32_all_variants]
|
||||
lib_deps =
|
||||
willmmiles/AsyncTCP @ 1.3.1
|
||||
bitbank2/AnimatedGIF@^1.4.7
|
||||
https://github.com/Aircoookie/GifDecoder#bc3af18
|
||||
build_flags =
|
||||
-D CONFIG_ASYNC_TCP_USE_WDT=0
|
||||
-D WLED_ENABLE_GIF
|
||||
|
||||
[esp32]
|
||||
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.3/platform-espressif32-2.0.2.3.zip
|
||||
platform = espressif32@3.5.0
|
||||
platform_packages = framework-arduinoespressif32 @ https://github.com/Aircoookie/arduino-esp32.git#1.0.6.4
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = -g
|
||||
-DARDUINO_ARCH_ESP32
|
||||
#-DCONFIG_LITTLEFS_FOR_IDF_3_2
|
||||
#use LITTLEFS library by lorol in ESP32 core 1.x.x instead of built-in in 2.x.x
|
||||
-D LOROL_LITTLEFS
|
||||
; -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
|
||||
${esp32_all_variants.build_flags}
|
||||
|
||||
tiny_partitions = tools/WLED_ESP32_2MB_noOTA.csv
|
||||
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
|
||||
extended_partitions = tools/WLED_ESP32_4MB_700k_FS.csv
|
||||
big_partitions = tools/WLED_ESP32_4MB_256KB_FS.csv ;; 1.8MB firmware, 256KB filesystem, coredump support
|
||||
large_partitions = tools/WLED_ESP32_8MB.csv
|
||||
extreme_partitions = tools/WLED_ESP32_16MB_9MB_FS.csv
|
||||
lib_deps =
|
||||
https://github.com/lorol/LITTLEFS.git
|
||||
${esp32_all_variants.lib_deps}
|
||||
${env.lib_deps}
|
||||
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
|
||||
# additional build flags for audioreactive - must be applied globally
|
||||
AR_build_flags = ;; -fsingle-precision-constant ;; forces ArduinoFFT to use float math (2x faster)
|
||||
AR_lib_deps = ;; for pre-usermod-library platformio_override compatibility
|
||||
|
||||
|
||||
[esp32_idf_V4]
|
||||
;; experimental build environment for ESP32 using ESP-IDF 4.4.x / arduino-esp32 v2.0.5
|
||||
;; very similar to the normal ESP32 flags, but omitting Lorol LittleFS, as littlefs is included in the new framework already.
|
||||
;;
|
||||
;; please note that you can NOT update existing ESP32 installs with a "V4" build. Also updating by OTA will not work properly.
|
||||
;; You need to completely erase your device (esptool erase_flash) first, then install the "V4" build from VSCode+platformio.
|
||||
|
||||
;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them)
|
||||
platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.06.02/platform-espressif32.zip ;; Tasmota Arduino Core 2.0.9 with IPv6 support, based on IDF 4.4.4
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = -g
|
||||
-Wshadow=compatible-local ;; emit warning in case a local variable "shadows" another local one
|
||||
-DARDUINO_ARCH_ESP32 -DESP32
|
||||
-DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
|
||||
${esp32_all_variants.build_flags}
|
||||
-D WLED_ENABLE_DMX_INPUT
|
||||
lib_deps =
|
||||
${esp32_all_variants.lib_deps}
|
||||
https://github.com/someweisguy/esp_dmx.git#47db25d
|
||||
${env.lib_deps}
|
||||
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
|
||||
|
||||
[esp32s2]
|
||||
;; generic definitions for all ESP32-S2 boards
|
||||
platform = ${esp32_idf_V4.platform}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = -g
|
||||
-DARDUINO_ARCH_ESP32
|
||||
-DARDUINO_ARCH_ESP32S2
|
||||
-DCONFIG_IDF_TARGET_ESP32S2=1
|
||||
-DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0
|
||||
-DCO
|
||||
-DARDUINO_USB_MODE=0 ;; this flag is mandatory for ESP32-S2 !
|
||||
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
|
||||
;; ARDUINO_USB_CDC_ON_BOOT
|
||||
${esp32_all_variants.build_flags}
|
||||
lib_deps =
|
||||
${esp32_all_variants.lib_deps}
|
||||
${env.lib_deps}
|
||||
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
|
||||
|
||||
[esp32c3]
|
||||
;; generic definitions for all ESP32-C3 boards
|
||||
platform = ${esp32_idf_V4.platform}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = -g
|
||||
-DARDUINO_ARCH_ESP32
|
||||
-DARDUINO_ARCH_ESP32C3
|
||||
-DCONFIG_IDF_TARGET_ESP32C3=1
|
||||
-DCO
|
||||
-DARDUINO_USB_MODE=1 ;; this flag is mandatory for ESP32-C3
|
||||
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
|
||||
;; ARDUINO_USB_CDC_ON_BOOT
|
||||
${esp32_all_variants.build_flags}
|
||||
lib_deps =
|
||||
${esp32_all_variants.lib_deps}
|
||||
${env.lib_deps}
|
||||
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
|
||||
board_build.flash_mode = qio
|
||||
|
||||
[esp32s3]
|
||||
;; generic definitions for all ESP32-S3 boards
|
||||
platform = ${esp32_idf_V4.platform}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = -g
|
||||
-DESP32
|
||||
-DARDUINO_ARCH_ESP32
|
||||
-DARDUINO_ARCH_ESP32S3
|
||||
-DCONFIG_IDF_TARGET_ESP32S3=1
|
||||
-DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_DFU_ON_BOOT=0
|
||||
-DCO
|
||||
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
|
||||
;; ARDUINO_USB_MODE, ARDUINO_USB_CDC_ON_BOOT
|
||||
${esp32_all_variants.build_flags}
|
||||
lib_deps =
|
||||
${esp32_all_variants.lib_deps}
|
||||
${env.lib_deps}
|
||||
board_build.partitions = ${esp32.large_partitions} ;; default partioning for 8MB flash - can be overridden in build envs
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# WLED BUILDS
|
||||
@ -168,148 +355,309 @@ lib_ignore =
|
||||
|
||||
[env:nodemcuv2]
|
||||
board = nodemcuv2
|
||||
platform = ${common.platform_latest}
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP8266\" #-DWLED_DISABLE_2D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
monitor_filters = esp8266_exception_decoder
|
||||
|
||||
[env:esp01]
|
||||
board = esp01
|
||||
platform = ${common.platform_latest}
|
||||
board_build.ldscript = ${common.ldscript_512k}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA -D WLED_DISABLE_ALEXA -D WLED_DISABLE_BLYNK
|
||||
-D WLED_DISABLE_CRONIXIE -D WLED_DISABLE_HUESYNC -D WLED_DISABLE_INFRARED
|
||||
[env:nodemcuv2_compat]
|
||||
extends = env:nodemcuv2
|
||||
;; using platform version and build options from WLED 0.14.0
|
||||
platform = ${esp8266.platform_compat}
|
||||
platform_packages = ${esp8266.platform_packages_compat}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP8266_compat\" #-DWLED_DISABLE_2D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
;; lib_deps = ${esp8266.lib_deps_compat} ;; experimental - use older NeoPixelBus 2.7.9
|
||||
|
||||
[env:esp01_1m_ota]
|
||||
board = esp01_1m
|
||||
platform = ${common.platform_latest}
|
||||
board_build.ldscript = ${common.ldscript_1m0m}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_ALEXA -D WLED_DISABLE_BLYNK -D WLED_DISABLE_CRONIXIE -D WLED_DISABLE_HUESYNC -D WLED_DISABLE_INFRARED
|
||||
[env:nodemcuv2_160]
|
||||
extends = env:nodemcuv2
|
||||
board_build.f_cpu = 160000000L
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP8266_160\" #-DWLED_DISABLE_2D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
custom_usermods = audioreactive
|
||||
|
||||
[env:esp8266_2m]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP02\"
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM1D
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp8266_2m_compat]
|
||||
extends = env:esp8266_2m
|
||||
;; using platform version and build options from WLED 0.14.0
|
||||
platform = ${esp8266.platform_compat}
|
||||
platform_packages = ${esp8266.platform_packages_compat}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP02_compat\" #-DWLED_DISABLE_2D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM1D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
|
||||
[env:esp8266_2m_160]
|
||||
extends = env:esp8266_2m
|
||||
board_build.f_cpu = 160000000L
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP02_160\"
|
||||
-D WLED_DISABLE_PARTICLESYSTEM1D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
custom_usermods = audioreactive
|
||||
|
||||
[env:esp01_1m_full]
|
||||
board = esp01_1m
|
||||
platform = ${common.platform_latest}
|
||||
board_build.ldscript = ${common.ldscript_1m0m}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP01\" -D WLED_DISABLE_OTA
|
||||
; -D WLED_USE_REAL_MATH ;; may fix wrong sunset/sunrise times, at the cost of 7064 bytes FLASH and 975 bytes RAM
|
||||
-D WLED_DISABLE_PARTICLESYSTEM1D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp07]
|
||||
board = esp07
|
||||
platform = ${common.platform_latest}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
[env:esp01_1m_full_compat]
|
||||
extends = env:esp01_1m_full
|
||||
;; using platform version and build options from WLED 0.14.0
|
||||
platform = ${esp8266.platform_compat}
|
||||
platform_packages = ${esp8266.platform_packages_compat}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP01_compat\" -D WLED_DISABLE_OTA #-DWLED_DISABLE_2D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM1D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
|
||||
[env:d1_mini]
|
||||
board = d1_mini
|
||||
platform = ${common.platform_latest}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
|
||||
[env:heltec_wifi_kit_8]
|
||||
board = d1_mini
|
||||
platform = ${common.platform_latest}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
|
||||
[env:h803wf]
|
||||
board = d1_mini
|
||||
platform = ${common.platform_latest}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_flags = ${common.build_flags_esp8266} -D LEDPIN=1 -D WLED_DISABLE_INFRARED
|
||||
[env:esp01_1m_full_160]
|
||||
extends = env:esp01_1m_full
|
||||
board_build.f_cpu = 160000000L
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP01_160\" -D WLED_DISABLE_OTA
|
||||
; -D WLED_USE_REAL_MATH ;; may fix wrong sunset/sunrise times, at the cost of 7064 bytes FLASH and 975 bytes RAM
|
||||
-D WLED_DISABLE_PARTICLESYSTEM1D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
custom_usermods = audioreactive
|
||||
|
||||
[env:esp32dev]
|
||||
board = esp32dev
|
||||
platform = espressif32@1.11.2
|
||||
build_flags = ${common.build_flags_esp32}
|
||||
lib_ignore =
|
||||
ESPAsyncTCP
|
||||
ESPAsyncUDP
|
||||
platform = ${esp32.platform}
|
||||
platform_packages = ${esp32.platform_packages}
|
||||
custom_usermods = audioreactive
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32\" #-D WLED_DISABLE_BROWNOUT_DET
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
monitor_filters = esp32_exception_decoder
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
|
||||
[env:esp8285_4CH_MagicHome]
|
||||
board = esp8285
|
||||
platform = ${common.platform_latest}
|
||||
board_build.ldscript = ${common.ldscript_1m0m}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_HUESYNC -D WLED_USE_ANALOG_LEDS
|
||||
|
||||
[env:esp8285_4CH_H801]
|
||||
board = esp8285
|
||||
platform = ${common.platform_latest}
|
||||
board_build.ldscript = ${common.ldscript_1m0m}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_HUESYNC -D WLED_USE_ANALOG_LEDS -D WLED_USE_H801
|
||||
|
||||
[env:esp8285_5CH_H801]
|
||||
board = esp8285
|
||||
platform = ${common.platform_latest}
|
||||
board_build.ldscript = ${common.ldscript_1m0m}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_HUESYNC -D WLED_USE_ANALOG_LEDS -D WLED_USE_H801 -D WLED_ENABLE_5CH_LEDS
|
||||
|
||||
[env:d1_mini_5CH_Shojo_PCB]
|
||||
board = d1_mini
|
||||
platform = ${common.platform_latest}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_USE_ANALOG_LEDS -D SHOJO_PCB -D WLED_ENABLE_5CH_LEDS
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# DEVELOPMENT BOARDS
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
[env:d1_mini_debug]
|
||||
board = d1_mini
|
||||
build_type = debug
|
||||
platform = ${common.platform_latest}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_flags = ${common.build_flags_esp8266} ${common.debug_flags}
|
||||
|
||||
[env:d1_mini_ota]
|
||||
board = d1_mini
|
||||
upload_protocol = espota
|
||||
# exchange for your WLED IP
|
||||
upload_port = "10.10.1.27"
|
||||
platform = ${common.platform_latest}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# custom board configurations
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
[env:custom_LEDPIN_4]
|
||||
board = d1_mini
|
||||
platform = ${common.platform_latest}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_flags = ${common.build_flags_esp8266} -D LEDPIN=4 -D IRPIN=5
|
||||
|
||||
[env:custom_LEDPIN_16]
|
||||
board = d1_mini
|
||||
platform = ${common.platform_latest}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_flags = ${common.build_flags_esp8266} -D LEDPIN=16
|
||||
|
||||
[env:custom_APA102]
|
||||
board = d1_mini
|
||||
platform = ${common.platform_latest}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_flags = ${common.build_flags_esp8266} -D USE_APA102
|
||||
|
||||
[env:custom_WS2801]
|
||||
board = d1_mini
|
||||
platform = ${common.platform_latest}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_flags = ${common.build_flags_esp8266} -D USE_WS2801
|
||||
|
||||
[env:custom32_LEDPIN_16]
|
||||
[env:esp32dev_V4]
|
||||
board = esp32dev
|
||||
platform = espressif32@1.11.2
|
||||
build_flags = ${common.build_flags_esp32} -D LEDPIN=16
|
||||
lib_ignore =
|
||||
ESPAsyncTCP
|
||||
ESPAsyncUDP
|
||||
platform = ${esp32_idf_V4.platform}
|
||||
build_unflags = ${common.build_unflags}
|
||||
custom_usermods = audioreactive
|
||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_V4\" #-D WLED_DISABLE_BROWNOUT_DET
|
||||
lib_deps = ${esp32_idf_V4.lib_deps}
|
||||
monitor_filters = esp32_exception_decoder
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
board_build.flash_mode = dio
|
||||
|
||||
[env:wemos_shield_esp32]
|
||||
[env:esp32dev_8M]
|
||||
board = esp32dev
|
||||
platform = espressif32@1.11.2
|
||||
upload_port = /dev/cu.SLAB_USBtoUART
|
||||
monitor_port = /dev/cu.SLAB_USBtoUART
|
||||
platform = ${esp32_idf_V4.platform}
|
||||
custom_usermods = audioreactive
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_8M\" #-D WLED_DISABLE_BROWNOUT_DET
|
||||
lib_deps = ${esp32_idf_V4.lib_deps}
|
||||
monitor_filters = esp32_exception_decoder
|
||||
board_build.partitions = ${esp32.large_partitions}
|
||||
board_upload.flash_size = 8MB
|
||||
board_upload.maximum_size = 8388608
|
||||
; board_build.f_flash = 80000000L
|
||||
; board_build.flash_mode = qio
|
||||
|
||||
[env:esp32dev_16M]
|
||||
board = esp32dev
|
||||
platform = ${esp32_idf_V4.platform}
|
||||
custom_usermods = audioreactive
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_16M\" #-D WLED_DISABLE_BROWNOUT_DET
|
||||
lib_deps = ${esp32_idf_V4.lib_deps}
|
||||
monitor_filters = esp32_exception_decoder
|
||||
board_build.partitions = ${esp32.extreme_partitions}
|
||||
board_upload.flash_size = 16MB
|
||||
board_upload.maximum_size = 16777216
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = dio
|
||||
|
||||
;[env:esp32dev_audioreactive]
|
||||
;board = esp32dev
|
||||
;platform = ${esp32.platform}
|
||||
;platform_packages = ${esp32.platform_packages}
|
||||
;custom_usermods = audioreactive
|
||||
;build_unflags = ${common.build_unflags}
|
||||
;build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32_audioreactive\" #-D WLED_DISABLE_BROWNOUT_DET
|
||||
;lib_deps = ${esp32.lib_deps}
|
||||
;monitor_filters = esp32_exception_decoder
|
||||
;board_build.partitions = ${esp32.default_partitions}
|
||||
;; board_build.f_flash = 80000000L
|
||||
;; board_build.flash_mode = dio
|
||||
|
||||
[env:esp32_eth]
|
||||
board = esp32-poe
|
||||
platform = ${esp32.platform}
|
||||
platform_packages = ${esp32.platform_packages}
|
||||
upload_speed = 921600
|
||||
custom_usermods = audioreactive
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32_Ethernet\" -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1
|
||||
; -D WLED_DISABLE_ESPNOW ;; ESP-NOW requires wifi, may crash with ethernet only
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
|
||||
[env:esp32_wrover]
|
||||
extends = esp32_idf_V4
|
||||
platform = ${esp32_idf_V4.platform}
|
||||
board = ttgo-t7-v14-mini32
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = qio
|
||||
board_build.partitions = ${esp32.extended_partitions}
|
||||
custom_usermods = audioreactive
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_WROVER\"
|
||||
-DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue ;; Older ESP32 (rev.<3) need a PSRAM fix (increases static RAM used) https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/external-ram.html
|
||||
-D DATA_PINS=25
|
||||
lib_deps = ${esp32_idf_V4.lib_deps}
|
||||
|
||||
[env:esp32c3dev]
|
||||
extends = esp32c3
|
||||
platform = ${esp32c3.platform}
|
||||
framework = arduino
|
||||
board = esp32-c3-devkitm-1
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-C3\"
|
||||
-D WLED_WATCHDOG_TIMEOUT=0
|
||||
-DLOLIN_WIFI_FIX ; seems to work much better with this
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB
|
||||
;-DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip
|
||||
upload_speed = 460800
|
||||
build_flags = ${common.build_flags_esp32} -D LEDPIN=16 -D RLYPIN=19 -D BTNPIN=17
|
||||
lib_ignore =
|
||||
ESPAsyncTCP
|
||||
ESPAsyncUDP
|
||||
build_unflags = ${common.build_unflags}
|
||||
lib_deps = ${esp32c3.lib_deps}
|
||||
|
||||
[env:esp32s3dev_16MB_opi]
|
||||
;; ESP32-S3 development board, with 16MB FLASH and >= 8MB PSRAM (memory_type: qio_opi)
|
||||
board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support
|
||||
board_build.arduino.memory_type = qio_opi ;; use with PSRAM: 8MB or 16MB
|
||||
platform = ${esp32s3.platform}
|
||||
upload_speed = 921600
|
||||
custom_usermods = audioreactive
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_16MB_opi\"
|
||||
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
|
||||
;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
|
||||
-D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
|
||||
-DBOARD_HAS_PSRAM
|
||||
lib_deps = ${esp32s3.lib_deps}
|
||||
board_build.partitions = ${esp32.extreme_partitions}
|
||||
board_upload.flash_size = 16MB
|
||||
board_upload.maximum_size = 16777216
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = qio
|
||||
monitor_filters = esp32_exception_decoder
|
||||
|
||||
[env:esp32s3dev_8MB_opi]
|
||||
;; ESP32-S3 development board, with 8MB FLASH and >= 8MB PSRAM (memory_type: qio_opi)
|
||||
board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support
|
||||
board_build.arduino.memory_type = qio_opi ;; use with PSRAM: 8MB or 16MB
|
||||
platform = ${esp32s3.platform}
|
||||
upload_speed = 921600
|
||||
custom_usermods = audioreactive
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_8MB_opi\"
|
||||
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
|
||||
;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
|
||||
-D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
|
||||
-DBOARD_HAS_PSRAM
|
||||
lib_deps = ${esp32s3.lib_deps}
|
||||
board_build.partitions = ${esp32.large_partitions}
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = qio
|
||||
monitor_filters = esp32_exception_decoder
|
||||
|
||||
[env:esp32S3_wroom2]
|
||||
;; For ESP32-S3 WROOM-2, a.k.a. ESP32-S3 DevKitC-1 v1.1
|
||||
;; with >= 16MB FLASH and >= 8MB PSRAM (memory_type: opi_opi)
|
||||
platform = ${esp32s3.platform}
|
||||
board = esp32s3camlcd ;; this is the only standard board with "opi_opi"
|
||||
board_build.arduino.memory_type = opi_opi
|
||||
upload_speed = 921600
|
||||
custom_usermods = audioreactive
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_WROOM-2\"
|
||||
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
|
||||
-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
|
||||
;; -D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
|
||||
-DBOARD_HAS_PSRAM
|
||||
-D LEDPIN=38 -D DATA_PINS=38 ;; buildin WS2812b LED
|
||||
-D BTNPIN=0 -D RLYPIN=16 -D IRPIN=17 -D AUDIOPIN=-1
|
||||
-D WLED_DEBUG
|
||||
-D SR_DMTYPE=1 -D I2S_SDPIN=13 -D I2S_CKPIN=14 -D I2S_WSPIN=15 -D MCLK_PIN=4 ;; I2S mic
|
||||
lib_deps = ${esp32s3.lib_deps}
|
||||
|
||||
board_build.partitions = ${esp32.extreme_partitions}
|
||||
board_upload.flash_size = 16MB
|
||||
board_upload.maximum_size = 16777216
|
||||
monitor_filters = esp32_exception_decoder
|
||||
|
||||
[env:esp32s3_4M_qspi]
|
||||
;; ESP32-S3, with 4MB FLASH and <= 4MB PSRAM (memory_type: qio_qspi)
|
||||
board = lolin_s3_mini ;; -S3 mini, 4MB flash 2MB PSRAM
|
||||
platform = ${esp32s3.platform}
|
||||
upload_speed = 921600
|
||||
custom_usermods = audioreactive
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_4M_qspi\"
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
|
||||
-DBOARD_HAS_PSRAM
|
||||
-DLOLIN_WIFI_FIX ; seems to work much better with this
|
||||
-D WLED_WATCHDOG_TIMEOUT=0
|
||||
lib_deps = ${esp32s3.lib_deps}
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = qio
|
||||
monitor_filters = esp32_exception_decoder
|
||||
|
||||
[env:lolin_s2_mini]
|
||||
platform = ${esp32s2.platform}
|
||||
board = lolin_s2_mini
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
board_build.flash_mode = qio
|
||||
board_build.f_flash = 80000000L
|
||||
custom_usermods = audioreactive
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S2\"
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
-DARDUINO_USB_MSC_ON_BOOT=0
|
||||
-DARDUINO_USB_DFU_ON_BOOT=0
|
||||
-DBOARD_HAS_PSRAM
|
||||
-DLOLIN_WIFI_FIX ; seems to work much better with this
|
||||
-D WLED_WATCHDOG_TIMEOUT=0
|
||||
-D DATA_PINS=16
|
||||
-D HW_PIN_SCL=35
|
||||
-D HW_PIN_SDA=33
|
||||
-D HW_PIN_CLOCKSPI=7
|
||||
-D HW_PIN_DATASPI=11
|
||||
-D HW_PIN_MISOSPI=9
|
||||
; -D STATUSLED=15
|
||||
lib_deps = ${esp32s2.lib_deps}
|
||||
|
||||
|
||||
[env:usermods]
|
||||
board = esp32dev
|
||||
platform = ${esp32_idf_V4.platform}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_USERMODS\"
|
||||
-DTOUCH_CS=9
|
||||
lib_deps = ${esp32_idf_V4.lib_deps}
|
||||
monitor_filters = esp32_exception_decoder
|
||||
board_build.flash_mode = dio
|
||||
; custom_usermods = *every folder with library.json* -- injected by pio-scripts/load_usermods.py
|
||||
board_build.partitions = ${esp32.extreme_partitions} ; We're gonna need a bigger boat
|
||||
|
@ -1,36 +0,0 @@
|
||||
# Example PlatformIO Project Configuration Override
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copy to platformio_override.ini to activate overrides
|
||||
# ------------------------------------------------------------------------------
|
||||
# Please visit documentation: https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:esp8266_1m_custom]
|
||||
board = esp01_1m
|
||||
platform = ${common.arduino_core_2_4_2}
|
||||
board_build.ldscript = ${common.ldscript_1m0m}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
-D WLED_DISABLE_OTA
|
||||
-D WLED_DISABLE_ALEXA
|
||||
-D WLED_DISABLE_BLYNK
|
||||
-D WLED_DISABLE_CRONIXIE
|
||||
-D WLED_DISABLE_HUESYNC
|
||||
-D WLED_DISABLE_INFRARED
|
||||
; PIN defines - uncomment and change, if needed:
|
||||
; -D LEDPIN=2
|
||||
; -D BTNPIN=0
|
||||
; -D IR_PIN=4
|
||||
; -D RLYPIN=12
|
||||
; -D RLYMDE=1
|
||||
; digital LED strip types - uncomment only one ! - this will disable WS281x / SK681x support
|
||||
; -D USE_APA102
|
||||
; -D USE_WS2801
|
||||
; -D USE_LPD8806
|
||||
; to drive analog LED strips (aka 5050), uncomment the following
|
||||
; PWM pins 5,12,13,15 are used with Magic Home LED Controller (default)
|
||||
; -D WLED_USE_ANALOG_LEDS
|
||||
; for the H801 controller (PINs 15,13,12,14 (W2 = 04)) uncomment this
|
||||
; -D WLED_USE_H801
|
||||
; for the BW-LT11 controller (PINs 12,4,14,5 ) uncomment this
|
||||
; -D WLED_USE_BWLT11
|
||||
; and to enable channel 5 for RGBW-CT led strips this
|
||||
; -D WLED_USE_5CH_LEDS
|
538
platformio_override.sample.ini
Normal file
@ -0,0 +1,538 @@
|
||||
# Example PlatformIO Project Configuration Override
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copy to platformio_override.ini to activate overrides
|
||||
# ------------------------------------------------------------------------------
|
||||
# Please visit documentation: https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[platformio]
|
||||
default_envs = WLED_generic8266_1M, esp32dev_V4_dio80 # put the name(s) of your own build environment here. You can define as many as you need
|
||||
|
||||
#----------
|
||||
# SAMPLE
|
||||
#----------
|
||||
[env:WLED_generic8266_1M]
|
||||
extends = env:esp01_1m_full # when you want to extend the existing environment (define only updated options)
|
||||
; board = esp01_1m # uncomment when ou need different board
|
||||
; platform = ${common.platform_wled_default} # uncomment and change when you want particular platform
|
||||
; platform_packages = ${common.platform_packages}
|
||||
; board_build.ldscript = ${common.ldscript_1m128k}
|
||||
; upload_speed = 921600 # fast upload speed (remove ';' if your board supports fast upload speed)
|
||||
# Sample libraries used for various usermods. Uncomment when using particular usermod.
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
; olikraus/U8g2 # @~2.33.15
|
||||
; paulstoffregen/OneWire@~2.3.8
|
||||
; adafruit/Adafruit Unified Sensor@^1.1.4
|
||||
; adafruit/DHT sensor library@^1.4.1
|
||||
; adafruit/Adafruit BME280 Library@^2.2.2
|
||||
; Wire
|
||||
; robtillaart/SHT85@~0.3.3
|
||||
; ;gmag11/QuickESPNow @ ~0.7.0 # will also load QuickDebug
|
||||
; https://github.com/blazoncek/QuickESPNow.git#optional-debug ;; exludes debug library
|
||||
; bitbank2/PNGdec@^1.0.1 ;; used for POV display uncomment following
|
||||
; ${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE
|
||||
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags}
|
||||
;
|
||||
; *** To use the below defines/overrides, copy and paste each onto its own line just below build_flags in the section above.
|
||||
;
|
||||
; Set a release name that may be used to distinguish required binary for flashing
|
||||
; -D WLED_RELEASE_NAME=\"ESP32_MULTI_USREMODS\"
|
||||
;
|
||||
; disable specific features
|
||||
; -D WLED_DISABLE_OTA
|
||||
; -D WLED_DISABLE_ALEXA
|
||||
; -D WLED_DISABLE_HUESYNC
|
||||
; -D WLED_DISABLE_LOXONE
|
||||
; -D WLED_DISABLE_INFRARED
|
||||
; -D WLED_DISABLE_WEBSOCKETS
|
||||
; -D WLED_DISABLE_MQTT
|
||||
; -D WLED_DISABLE_ADALIGHT
|
||||
; -D WLED_DISABLE_2D
|
||||
; -D WLED_DISABLE_PXMAGIC
|
||||
; -D WLED_DISABLE_ESPNOW
|
||||
; -D WLED_DISABLE_BROWNOUT_DET
|
||||
;
|
||||
; enable optional built-in features
|
||||
; -D WLED_ENABLE_PIXART
|
||||
; -D WLED_ENABLE_USERMOD_PAGE # if created
|
||||
; -D WLED_ENABLE_DMX
|
||||
;
|
||||
; PIN defines - uncomment and change, if needed:
|
||||
; -D DATA_PINS=2
|
||||
; or use this for multiple outputs
|
||||
; -D DATA_PINS=1,3
|
||||
; -D BTNPIN=0
|
||||
; -D IRPIN=4
|
||||
; -D RLYPIN=12
|
||||
; -D RLYMDE=1
|
||||
; -D RLYODRAIN=0
|
||||
; -D LED_BUILTIN=2 # GPIO of built-in LED
|
||||
;
|
||||
; Limit max buses
|
||||
; -D WLED_MAX_BUSSES=2
|
||||
; -D WLED_MAX_ANALOG_CHANNELS=3 # only 3 PWM HW pins available
|
||||
; -D WLED_MAX_DIGITAL_CHANNELS=2 # only 2 HW accelerated pins available
|
||||
;
|
||||
; Configure default WiFi
|
||||
; -D CLIENT_SSID='"MyNetwork"'
|
||||
; -D CLIENT_PASS='"Netw0rkPassw0rd"'
|
||||
;
|
||||
; Configure and use Ethernet
|
||||
; -D WLED_USE_ETHERNET
|
||||
; -D WLED_ETH_DEFAULT=5
|
||||
; do not use pins 5, (16,) 17, 18, 19, 21, 22, 23, 25, 26, 27 for anything but ethernet
|
||||
; -D PHY_ADDR=0 -D ETH_PHY_POWER=5 -D ETH_PHY_MDC=23 -D ETH_PHY_MDIO=18
|
||||
; -D ETH_CLK_MODE=ETH_CLOCK_GPIO17_OUT
|
||||
;
|
||||
; NTP time configuration
|
||||
; -D WLED_NTP_ENABLED=true
|
||||
; -D WLED_TIMEZONE=2
|
||||
; -D WLED_LAT=48.86
|
||||
; -D WLED_LON=2.33
|
||||
;
|
||||
; Use Watchdog timer with 10s guard
|
||||
; -D WLED_WATCHDOG_TIMEOUT=10
|
||||
;
|
||||
; Create debug build (with remote debug)
|
||||
; -D WLED_DEBUG
|
||||
; -D WLED_DEBUG_HOST='"192.168.0.100"'
|
||||
; -D WLED_DEBUG_PORT=7868
|
||||
;
|
||||
; Use Autosave usermod and set it to do save after 90s
|
||||
; -D USERMOD_AUTO_SAVE
|
||||
; -D AUTOSAVE_AFTER_SEC=90
|
||||
;
|
||||
; Use AHT10/AHT15/AHT20 usermod
|
||||
; -D USERMOD_AHT10
|
||||
;
|
||||
; Use INA226 usermod
|
||||
; -D USERMOD_INA226
|
||||
;
|
||||
; Use 4 Line Display usermod with SPI display
|
||||
; -D USERMOD_FOUR_LINE_DISPLAY
|
||||
; -DFLD_SPI_DEFAULT
|
||||
; -D FLD_TYPE=SSD1306_SPI64
|
||||
; -D FLD_PIN_CLOCKSPI=14
|
||||
; -D FLD_PIN_DATASPI=13
|
||||
; -D FLD_PIN_DC=26
|
||||
; -D FLD_PIN_CS=15
|
||||
; -D FLD_PIN_RESET=27
|
||||
;
|
||||
; Use Rotary encoder usermod (in conjunction with 4LD)
|
||||
; -D USERMOD_ROTARY_ENCODER_UI
|
||||
; -D ENCODER_DT_PIN=5
|
||||
; -D ENCODER_CLK_PIN=18
|
||||
; -D ENCODER_SW_PIN=19
|
||||
;
|
||||
; Use Dallas DS18B20 temperature sensor usermod and configure it to use GPIO13
|
||||
; -D USERMOD_DALLASTEMPERATURE
|
||||
; -D TEMPERATURE_PIN=13
|
||||
;
|
||||
; Use Multi Relay usermod and configure it to use 6 relays and appropriate GPIO
|
||||
; -D USERMOD_MULTI_RELAY
|
||||
; -D MULTI_RELAY_MAX_RELAYS=6
|
||||
; -D MULTI_RELAY_PINS=12,23,22,21,24,25
|
||||
;
|
||||
; Use PIR sensor usermod and configure it to use GPIO4 and timer of 60s
|
||||
; -D USERMOD_PIRSWITCH
|
||||
; -D PIR_SENSOR_PIN=4 # use -1 to disable usermod
|
||||
; -D PIR_SENSOR_OFF_SEC=60
|
||||
; -D PIR_SENSOR_MAX_SENSORS=2 # max allowable sensors (uses OR logic for triggering)
|
||||
;
|
||||
; Use Audioreactive usermod and configure I2S microphone
|
||||
; ${esp32.AR_build_flags} ;; default flags required to properly configure ArduinoFFT
|
||||
; ;; don't forget to add ArduinoFFT to your libs_deps: ${esp32.AR_lib_deps}
|
||||
; -D AUDIOPIN=-1
|
||||
; -D DMTYPE=1 # 0-analog/disabled, 1-I2S generic, 2-ES7243, 3-SPH0645, 4-I2S+mclk, 5-I2S PDM
|
||||
; -D I2S_SDPIN=36
|
||||
; -D I2S_WSPIN=23
|
||||
; -D I2S_CKPIN=19
|
||||
;
|
||||
; Use PWM fan usermod
|
||||
; -D USERMOD_PWM_FAN
|
||||
; -D TACHO_PIN=33
|
||||
; -D PWM_PIN=32
|
||||
;
|
||||
; Use POV Display usermod
|
||||
; -D USERMOD_POV_DISPLAY
|
||||
; Use built-in or custom LED as a status indicator (assumes LED is connected to GPIO16)
|
||||
; -D STATUSLED=16
|
||||
;
|
||||
; set the name of the module - make sure there is a quote-backslash-quote before the name and a backslash-quote-quote after the name
|
||||
; -D SERVERNAME="\"WLED\""
|
||||
;
|
||||
; set the number of LEDs
|
||||
; -D PIXEL_COUNTS=30
|
||||
; or this for multiple outputs
|
||||
; -D PIXEL_COUNTS=30,30
|
||||
;
|
||||
; set the default LED type
|
||||
; -D LED_TYPES=22 # see const.h (TYPE_xxxx)
|
||||
; or this for multiple outputs
|
||||
; -D LED_TYPES=TYPE_SK6812_RGBW,TYPE_WS2812_RGB
|
||||
;
|
||||
; set default color order of your led strip
|
||||
; -D DEFAULT_LED_COLOR_ORDER=COL_ORDER_GRB
|
||||
;
|
||||
; set milliampere limit when using ESP power pin (or inadequate PSU) to power LEDs
|
||||
; -D ABL_MILLIAMPS_DEFAULT=850
|
||||
; -D LED_MILLIAMPS_DEFAULT=55
|
||||
;
|
||||
; enable IR by setting remote type
|
||||
; -D IRTYPE=0 # 0 Remote disabled | 1 24-key RGB | 2 24-key with CT | 3 40-key blue | 4 40-key RGB | 5 21-key RGB | 6 6-key black | 7 9-key red | 8 JSON remote
|
||||
;
|
||||
; use PSRAM on classic ESP32 rev.1 (rev.3 or above has no issues)
|
||||
; -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue # needed only for classic ESP32 rev.1
|
||||
;
|
||||
; configure I2C and SPI interface (for various hardware)
|
||||
; -D I2CSDAPIN=33 # initialise interface
|
||||
; -D I2CSCLPIN=35 # initialise interface
|
||||
; -D HW_PIN_SCL=35
|
||||
; -D HW_PIN_SDA=33
|
||||
; -D HW_PIN_CLOCKSPI=7
|
||||
; -D HW_PIN_DATASPI=11
|
||||
; -D HW_PIN_MISOSPI=9
|
||||
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# PRE-CONFIGURED DEVELOPMENT BOARDS AND CONTROLLERS
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
[env:esp07]
|
||||
board = esp07
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:d1_mini]
|
||||
board = d1_mini
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
upload_speed = 921600
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
monitor_filters = esp8266_exception_decoder
|
||||
|
||||
[env:heltec_wifi_kit_8]
|
||||
board = d1_mini
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:h803wf]
|
||||
board = d1_mini
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=1 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp32dev_qio80]
|
||||
extends = env:esp32dev # we want to extend the existing esp32dev environment (and define only updated options)
|
||||
board = esp32dev
|
||||
build_flags = ${common.build_flags} ${esp32.build_flags} #-D WLED_DISABLE_BROWNOUT_DET
|
||||
${esp32.AR_build_flags} ;; optional - includes USERMOD_AUDIOREACTIVE
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE
|
||||
monitor_filters = esp32_exception_decoder
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = qio
|
||||
|
||||
[env:esp32dev_V4_dio80]
|
||||
;; experimental ESP32 env using ESP-IDF V4.4.x
|
||||
;; Warning: this build environment is not stable!!
|
||||
;; please erase your device before installing.
|
||||
extends = esp32_idf_V4 # based on newer "esp-idf V4" platform environment
|
||||
board = esp32dev
|
||||
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} #-D WLED_DISABLE_BROWNOUT_DET
|
||||
${esp32.AR_build_flags} ;; includes USERMOD_AUDIOREACTIVE
|
||||
lib_deps = ${esp32_idf_V4.lib_deps}
|
||||
${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE
|
||||
monitor_filters = esp32_exception_decoder
|
||||
board_build.partitions = ${esp32.default_partitions} ;; if you get errors about "out of program space", change this to ${esp32.extended_partitions} or even ${esp32.big_partitions}
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = dio
|
||||
|
||||
[env:esp32s2_saola]
|
||||
extends = esp32s2
|
||||
board = esp32-s2-saola-1
|
||||
platform = ${esp32s2.platform}
|
||||
platform_packages = ${esp32s2.platform_packages}
|
||||
framework = arduino
|
||||
board_build.flash_mode = qio
|
||||
upload_speed = 460800
|
||||
build_flags = ${common.build_flags} ${esp32s2.build_flags}
|
||||
;-DLOLIN_WIFI_FIX ;; try this in case Wifi does not work
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
lib_deps = ${esp32s2.lib_deps}
|
||||
|
||||
[env:esp32s3dev_8MB_PSRAM_qspi]
|
||||
;; ESP32-TinyS3 development board, with 8MB FLASH and PSRAM (memory_type: qio_qspi)
|
||||
extends = env:esp32s3dev_8MB_PSRAM_opi
|
||||
;board = um_tinys3 ; -> needs workaround from https://github.com/wled-dev/WLED/pull/2905#issuecomment-1328049860
|
||||
board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support
|
||||
board_build.arduino.memory_type = qio_qspi ;; use with PSRAM: 2MB or 4MB
|
||||
|
||||
[env:esp8285_4CH_MagicHome]
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_DISABLE_OTA
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp8285_H801]
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_DISABLE_OTA
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:d1_mini_5CH_Shojo_PCB]
|
||||
board = d1_mini
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_USE_SHOJO_PCB ;; NB: WLED_USE_SHOJO_PCB is not used anywhere in the source code. Not sure why its needed.
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:d1_mini_debug]
|
||||
board = d1_mini
|
||||
build_type = debug
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} ${common.debug_flags}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:d1_mini_ota]
|
||||
board = d1_mini
|
||||
upload_protocol = espota
|
||||
# exchange for your WLED IP
|
||||
upload_port = "10.10.1.27"
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:anavi_miracle_controller]
|
||||
board = d1_mini
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=12 -D IRPIN=-1 -D RLYPIN=2
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp32c3dev_2MB]
|
||||
;; for ESP32-C3 boards with 2MB flash (instead of 4MB).
|
||||
;; this board need a specific partition file. OTA not possible.
|
||||
extends = esp32c3
|
||||
platform = ${esp32c3.platform}
|
||||
platform_packages = ${esp32c3.platform_packages}
|
||||
board = esp32-c3-devkitm-1
|
||||
build_flags = ${common.build_flags} ${esp32c3.build_flags}
|
||||
-D WLED_WATCHDOG_TIMEOUT=0
|
||||
-D WLED_DISABLE_OTA
|
||||
; -DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB
|
||||
-DARDUINO_USB_CDC_ON_BOOT=0 ;; for serial-to-USB chip
|
||||
build_unflags = ${common.build_unflags}
|
||||
upload_speed = 115200
|
||||
lib_deps = ${esp32c3.lib_deps}
|
||||
board_build.partitions = tools/WLED_ESP32_2MB_noOTA.csv
|
||||
board_build.flash_mode = dio
|
||||
board_upload.flash_size = 2MB
|
||||
board_upload.maximum_size = 2097152
|
||||
|
||||
[env:wemos_shield_esp32]
|
||||
extends = esp32 ;; use default esp32 platform
|
||||
board = esp32dev
|
||||
upload_speed = 460800
|
||||
build_flags = ${common.build_flags} ${esp32.build_flags}
|
||||
-D WLED_RELEASE_NAME=\"ESP32_wemos_shield\"
|
||||
-D DATA_PINS=16
|
||||
-D RLYPIN=19
|
||||
-D BTNPIN=17
|
||||
-D IRPIN=18
|
||||
-UWLED_USE_MY_CONFIG
|
||||
-D USERMOD_DALLASTEMPERATURE
|
||||
-D USERMOD_FOUR_LINE_DISPLAY
|
||||
-D TEMPERATURE_PIN=23
|
||||
${esp32.AR_build_flags} ;; includes USERMOD_AUDIOREACTIVE
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
OneWire@~2.3.5 ;; needed for USERMOD_DALLASTEMPERATURE
|
||||
olikraus/U8g2 @ ^2.28.8 ;; needed for USERMOD_FOUR_LINE_DISPLAY
|
||||
${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
|
||||
[env:esp32_pico-D4]
|
||||
extends = esp32 ;; use default esp32 platform
|
||||
board = pico32 ;; pico32-D4 is different from the standard esp32dev
|
||||
;; hardware details from https://github.com/srg74/WLED-ESP32-pico
|
||||
build_flags = ${common.build_flags} ${esp32.build_flags}
|
||||
-D WLED_RELEASE_NAME=\"pico32-D4\" -D SERVERNAME='"WLED-pico32"'
|
||||
-D WLED_DISABLE_ADALIGHT ;; no serial-to-USB chip on this board - better to disable serial protocols
|
||||
-D DATA_PINS=2,18 ;; LED pins
|
||||
-D RLYPIN=19 -D BTNPIN=0 -D IRPIN=-1 ;; no default pin for IR
|
||||
${esp32.AR_build_flags} ;; include USERMOD_AUDIOREACTIVE
|
||||
-D UM_AUDIOREACTIVE_ENABLE ;; enable AR by default
|
||||
;; Audioreactive settings for on-board microphone (ICS-43432)
|
||||
-D SR_DMTYPE=1 -D I2S_SDPIN=25 -D I2S_WSPIN=15 -D I2S_CKPIN=14
|
||||
-D SR_SQUELCH=5 -D SR_GAIN=30
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
board_build.f_flash = 80000000L
|
||||
|
||||
[env:m5atom]
|
||||
extends = env:esp32dev # we want to extend the existing esp32dev environment (and define only updated options)
|
||||
build_flags = ${common.build_flags} ${esp32.build_flags} -D DATA_PINS=27 -D BTNPIN=39
|
||||
|
||||
[env:sp501e]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=3 -D BTNPIN=1
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:sp511e]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=3 -D BTNPIN=2 -D IRPIN=5 -D WLED_MAX_BUTTONS=3
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:Athom_RGBCW] ;7w and 5w(GU10) bulbs
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,13,5
|
||||
-D LED_TYPES=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:Athom_15w_RGBCW] ;15w bulb
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,5,13
|
||||
-D LED_TYPES=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 -D WLED_USE_IC_CCT
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:Athom_3Pin_Controller] ;small controller with only data
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=0 -D RLYPIN=-1 -D DATA_PINS=1 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:Athom_4Pin_Controller] ; With clock and data interface
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=0 -D RLYPIN=12 -D DATA_PINS=1 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:Athom_5Pin_Controller] ;Analog light strip controller
|
||||
board = esp8285
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=0 -D RLYPIN=-1 DATA_PINS=4,12,14,13 -D WLED_DISABLE_INFRARED
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:MY9291]
|
||||
board = esp01_1m
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_DISABLE_OTA -D USERMOD_MY9291
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# codm pixel controller board configurations
|
||||
# codm-controller-0_6 can also be used for the TYWE3S controller
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
[env:codm-controller-0_6]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_2m512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:codm-controller-0_6-rev2]
|
||||
board = esp_wroom_02
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags} ${esp8266.build_flags}
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# EleksTube-IPS
|
||||
# ------------------------------------------------------------------------------
|
||||
[env:elekstube_ips]
|
||||
extends = esp32 ;; use default esp32 platform
|
||||
board = esp32dev
|
||||
upload_speed = 921600
|
||||
custom_usermods = ${env:esp32dev.custom_usermods} RTC EleksTube_IPS
|
||||
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED
|
||||
-D DATA_PINS=12
|
||||
-D RLYPIN=27
|
||||
-D BTNPIN=34
|
||||
-D PIXEL_COUNTS=6
|
||||
# Display config
|
||||
-D ST7789_DRIVER
|
||||
-D TFT_WIDTH=135
|
||||
-D TFT_HEIGHT=240
|
||||
-D CGRAM_OFFSET
|
||||
-D TFT_SDA_READ
|
||||
-D TFT_MOSI=23
|
||||
-D TFT_SCLK=18
|
||||
-D TFT_DC=25
|
||||
-D TFT_RST=26
|
||||
-D SPI_FREQUENCY=40000000
|
||||
-D USER_SETUP_LOADED
|
||||
monitor_filters = esp32_exception_decoder
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Usermod examples
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# 433MHz RF remote example for esp32dev
|
||||
[env:esp32dev_usermod_RF433]
|
||||
extends = env:esp32dev
|
||||
build_flags = ${env:esp32dev.build_flags} -D USERMOD_RF433
|
||||
lib_deps = ${env:esp32dev.lib_deps}
|
||||
sui77/rc-switch @ 2.6.4
|
97
readme.md
@ -1,73 +1,84 @@
|
||||

|
||||
<p align="center">
|
||||
<img src="/images/wled_logo_akemi.png">
|
||||
<a href="https://github.com/wled-dev/WLED/releases"><img src="https://img.shields.io/github/release/wled-dev/WLED.svg?style=flat-square"></a>
|
||||
<a href="https://raw.githubusercontent.com/wled-dev/WLED/main/LICENSE"><img src="https://img.shields.io/github/license/wled-dev/wled?color=blue&style=flat-square"></a>
|
||||
<a href="https://wled.discourse.group"><img src="https://img.shields.io/discourse/topics?colorB=blue&label=forum&server=https%3A%2F%2Fwled.discourse.group%2F&style=flat-square"></a>
|
||||
<a href="https://discord.gg/QAh7wJHrRM"><img src="https://img.shields.io/discord/473448917040758787.svg?colorB=blue&label=discord&style=flat-square"></a>
|
||||
<a href="https://kno.wled.ge"><img src="https://img.shields.io/badge/quick_start-wiki-blue.svg?style=flat-square"></a>
|
||||
<a href="https://github.com/Aircoookie/WLED-App"><img src="https://img.shields.io/badge/app-wled-blue.svg?style=flat-square"></a>
|
||||
<a href="https://gitpod.io/#https://github.com/wled-dev/WLED"><img src="https://img.shields.io/badge/Gitpod-ready--to--code-blue?style=flat-square&logo=gitpod"></a>
|
||||
|
||||
[](https://github.com/Aircoookie/WLED/releases)
|
||||
[](https://wled.discourse.group)
|
||||
[](https://discord.gg/KuqP7NE)
|
||||
[](https://github.com/Aircoookie/WLED/wiki)
|
||||
[](https://github.com/Aircoookie/WLED-App)
|
||||
</p>
|
||||
|
||||
## Welcome to my project WLED!
|
||||
# Welcome to my project WLED! ✨
|
||||
|
||||
A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control NeoPixel (WS2812B, WS2811, SK6812, APA102) LEDs!
|
||||
A fast and feature-rich implementation of an ESP32 and ESP8266 webserver to control NeoPixel (WS2812B, WS2811, SK6812) LEDs or also SPI based chipsets like the WS2801 and APA102!
|
||||
|
||||
### Features:
|
||||
- WS2812FX library integrated for over 100 special effects
|
||||
## ⚙️ Features
|
||||
- WS2812FX library with more than 100 special effects
|
||||
- FastLED noise effects and 50 palettes
|
||||
- Modern UI with color, effect and segment controls
|
||||
- Segments to set different effects and colors to parts of the LEDs
|
||||
- Settings page - configuration over network
|
||||
- Segments to set different effects and colors to user defined parts of the LED string
|
||||
- Settings page - configuration via the network
|
||||
- Access Point and station mode - automatic failsafe AP
|
||||
- [Up to 10 LED outputs](https://kno.wled.ge/features/multi-strip/#esp32) per instance
|
||||
- Support for RGBW strips
|
||||
- 16 user presets to save and load colors/effects easily, supports cycling through them.
|
||||
- Macro functions to automatically execute API calls
|
||||
- Up to 250 user presets to save and load colors/effects easily, supports cycling through them.
|
||||
- Presets can be used to automatically execute API calls
|
||||
- Nightlight function (gradually dims down)
|
||||
- Full OTA software updatability (HTTP + ArduinoOTA), password protectable
|
||||
- Configurable analog clock + support for the Cronixie kit by Diamex
|
||||
- Configurable Auto Brightness limit for safer operation
|
||||
- Full OTA software updateability (HTTP + ArduinoOTA), password protectable
|
||||
- Configurable analog clock (Cronixie, 7-segment and EleksTube IPS clock support via usermods)
|
||||
- Configurable Auto Brightness limit for safe operation
|
||||
- Filesystem-based config for easier backup of presets and settings
|
||||
|
||||
### Supported light control interfaces:
|
||||
- WLED app for Android and iOS
|
||||
## 💡 Supported light control interfaces
|
||||
- WLED app for [Android](https://play.google.com/store/apps/details?id=com.aircoookie.WLED) and [iOS](https://apps.apple.com/us/app/wled/id1475695033)
|
||||
- JSON and HTTP request APIs
|
||||
- MQTT
|
||||
- Blynk IoT
|
||||
- E1.31
|
||||
- Hyperion
|
||||
- MQTT
|
||||
- E1.31, Art-Net, DDP and TPM2.net
|
||||
- [diyHue](https://github.com/diyhue/diyHue) (Wled is supported by diyHue, including Hue Sync Entertainment under udp. Thanks to [Gregory Mallios](https://github.com/gmallios))
|
||||
- [Hyperion](https://github.com/hyperion-project/hyperion.ng)
|
||||
- UDP realtime
|
||||
- Alexa voice control (including dimming and color)
|
||||
- Sync to Philips hue lights
|
||||
- Adalight (PC ambilight via serial)
|
||||
- Adalight (PC ambilight via serial) and TPM2
|
||||
- Sync color of multiple WLED devices (UDP notifier)
|
||||
- Infrared remotes (24-key RGB, receiver required)
|
||||
- Simple timers/schedules (time from NTP, timezones/DST supported)
|
||||
|
||||
### Quick start guide and documentation:
|
||||
## 📲 Quick start guide and documentation
|
||||
|
||||
See the [wiki](https://github.com/Aircoookie/WLED/wiki)!
|
||||
See the [documentation on our official site](https://kno.wled.ge)!
|
||||
|
||||
DrZzs has made some excellent video guides:
|
||||
[Introduction, hardware and installation](https://www.youtube.com/watch?v=tXvtxwK3jRk)
|
||||
[Settings, tips and tricks](https://www.youtube.com/watch?v=6eCE2BpLaUQ)
|
||||
[On this page](https://kno.wled.ge/basics/tutorials/) you can find excellent tutorials and tools to help you get your new project up and running!
|
||||
|
||||
If you'd rather read, here is a very [detailed step-by-step beginner tutorial](https://tynick.com/blog/11-03-2019/getting-started-with-wled-on-esp8266/) by tynick!
|
||||
## 🖼️ User interface
|
||||
<img src="/images/macbook-pro-space-gray-on-the-wooden-table.jpg" width="50%"><img src="/images/walking-with-iphone-x.jpg" width="50%">
|
||||
|
||||
Russian speakers, check out the videos by Room31:
|
||||
[WLED Firmware Overview: Interface and Settings](https://youtu.be/h7lKsczEI7E)
|
||||
[ESP8266 based LED controller for WS2812b strip. WLED Firmware + OpenHAB](https://youtu.be/K4ioTt3XvGc)
|
||||
## 💾 Compatible hardware
|
||||
|
||||
### Other
|
||||
See [here](https://kno.wled.ge/basics/compatible-hardware)!
|
||||
|
||||
Licensed under the MIT license
|
||||
Credits [here](https://github.com/Aircoookie/WLED/wiki/Contributors-&-About)!
|
||||
## ✌️ Other
|
||||
|
||||
Uses Linearicons by Perxis!
|
||||
Licensed under the EUPL v1.2 license
|
||||
Credits [here](https://kno.wled.ge/about/contributors/)!
|
||||
|
||||
Join the Discord server to discuss everything about WLED!
|
||||
|
||||
<a href="https://discord.gg/QAh7wJHrRM"><img src="https://discordapp.com/api/guilds/473448917040758787/widget.png?style=banner2" width="25%"></a>
|
||||
|
||||
Join the Discord [server](https://discord.gg/KuqP7NE) to discuss everything about WLED!
|
||||
Check out the WLED [Discourse forum](https://wled.discourse.group)!
|
||||
You can also send me mails to [dev.aircoookie@gmail.com](mailto:dev.aircoookie@gmail.com), but please only do so if you want to talk to me privately.
|
||||
If WLED really brightens up your every day, you can [](https://paypal.me/aircoookie)
|
||||
|
||||
You can also send me mails to [dev.aircoookie@gmail.com](mailto:dev.aircoookie@gmail.com), but please, only do so if you want to talk to me privately.
|
||||
|
||||
If WLED really brightens up your day, you can [](https://paypal.me/aircoookie)
|
||||
|
||||
|
||||
*Disclaimer:*
|
||||
If you are sensitive to photoeleptic seizures it is not recommended that you use this software.
|
||||
In case you still want to try, don't use strobe, lighting or noise modes or high effect speed settings.
|
||||
As per the MIT license, i assume no liability for any damage to you or any other person or equipment.
|
||||
|
||||
If you are prone to photosensitive epilepsy, we recommended you do **not** use this software.
|
||||
If you still want to try, don't use strobe, lighting or noise modes or high effect speed settings.
|
||||
|
||||
As per the EUPL license, I assume no liability for any damage to you or any other person or equipment.
|
||||
|
||||
|
1
requirements.in
Normal file
@ -0,0 +1 @@
|
||||
platformio>=6.1.17
|
58
requirements.txt
Normal file
@ -0,0 +1,58 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.11
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile requirements.in
|
||||
#
|
||||
ajsonrpc==1.2.0
|
||||
# via platformio
|
||||
anyio==4.8.0
|
||||
# via starlette
|
||||
bottle==0.13.2
|
||||
# via platformio
|
||||
certifi==2025.1.31
|
||||
# via requests
|
||||
charset-normalizer==3.4.1
|
||||
# via requests
|
||||
click==8.1.8
|
||||
# via
|
||||
# platformio
|
||||
# uvicorn
|
||||
colorama==0.4.6
|
||||
# via platformio
|
||||
h11==0.14.0
|
||||
# via
|
||||
# uvicorn
|
||||
# wsproto
|
||||
idna==3.10
|
||||
# via
|
||||
# anyio
|
||||
# requests
|
||||
marshmallow==3.26.1
|
||||
# via platformio
|
||||
packaging==24.2
|
||||
# via marshmallow
|
||||
platformio==6.1.17
|
||||
# via -r requirements.in
|
||||
pyelftools==0.32
|
||||
# via platformio
|
||||
pyserial==3.5
|
||||
# via platformio
|
||||
requests==2.32.3
|
||||
# via platformio
|
||||
semantic-version==2.10.0
|
||||
# via platformio
|
||||
sniffio==1.3.1
|
||||
# via anyio
|
||||
starlette==0.45.3
|
||||
# via platformio
|
||||
tabulate==0.9.0
|
||||
# via platformio
|
||||
typing-extensions==4.12.2
|
||||
# via anyio
|
||||
urllib3==2.3.0
|
||||
# via requests
|
||||
uvicorn==0.34.0
|
||||
# via platformio
|
||||
wsproto==1.2.0
|
||||
# via platformio
|
BIN
tools/AutoCubeMap.xlsx
Normal file
6
tools/WLED_ESP32-wrover_4MB.csv
Normal file
@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x1A0000,
|
||||
app1, app, ota_1, 0x1B0000,0x1A0000,
|
||||
spiffs, data, spiffs, 0x350000,0xB0000,
|
|
6
tools/WLED_ESP32_16MB.csv
Normal file
@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x200000,
|
||||
app1, app, ota_1, 0x210000,0x200000,
|
||||
spiffs, data, spiffs, 0x410000,0xBE0000,
|
|
8
tools/WLED_ESP32_16MB_9MB_FS.csv
Normal file
@ -0,0 +1,8 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x300000,
|
||||
app1, app, ota_1, 0x310000,0x300000,
|
||||
spiffs, data, spiffs, 0x610000,0x9E0000,
|
||||
coredump, data, coredump,,64K
|
||||
# to create/use ffat, see https://github.com/marcmerlin/esp32_fatfsimage
|
|
5
tools/WLED_ESP32_2MB_noOTA.csv
Normal file
@ -0,0 +1,5 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 20K,
|
||||
otadata, data, ota, 0xe000, 8K,
|
||||
app0, app, ota_0, 0x10000, 1536K,
|
||||
spiffs, data, spiffs, 0x190000, 384K,
|
|
6
tools/WLED_ESP32_4MB_1MB_FS.csv
Normal file
@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x180000,
|
||||
app1, app, ota_1, 0x190000,0x180000,
|
||||
spiffs, data, spiffs, 0x310000,0xF0000,
|
|
7
tools/WLED_ESP32_4MB_256KB_FS.csv
Normal file
@ -0,0 +1,7 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x1D0000,
|
||||
app1, app, ota_1, 0x1E0000,0x1D0000,
|
||||
spiffs, data, spiffs, 0x3B0000,0x40000,
|
||||
coredump, data, coredump,,64K
|
|
7
tools/WLED_ESP32_4MB_512KB_FS.csv
Normal file
@ -0,0 +1,7 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x1B0000,
|
||||
app1, app, ota_1, 0x1C0000,0x1B0000,
|
||||
spiffs, data, spiffs, 0x370000,0x80000,
|
||||
coredump, data, coredump,,64K
|
|
6
tools/WLED_ESP32_4MB_700k_FS.csv
Normal file
@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x1A0000,
|
||||
app1, app, ota_1, 0x1B0000,0x1A0000,
|
||||
spiffs, data, spiffs, 0x350000,0xB0000,
|
|
7
tools/WLED_ESP32_8MB.csv
Normal file
@ -0,0 +1,7 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x200000,
|
||||
app1, app, ota_1, 0x210000,0x200000,
|
||||
spiffs, data, spiffs, 0x410000,0x3E0000,
|
||||
coredump, data, coredump,,64K
|
|
17
tools/all_xml.sh
Normal file
@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
# Pull all settings pages for comparison
|
||||
HOST=$1
|
||||
TGT_PATH=$2
|
||||
CURL_ARGS="--compressed"
|
||||
|
||||
# Replicate one target many times
|
||||
function replicate() {
|
||||
for i in {0..10}
|
||||
do
|
||||
echo -n " http://${HOST}/settings.js?p=$i -o ${TGT_PATH}/$i.xml"
|
||||
done
|
||||
}
|
||||
read -a TARGETS <<< $(replicate)
|
||||
|
||||
mkdir -p ${TGT_PATH}
|
||||
curl ${CURL_ARGS} ${TARGETS[@]}
|
212
tools/cdata-test.js
Normal file
@ -0,0 +1,212 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('node:assert');
|
||||
const { describe, it, before, after } = require('node:test');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const child_process = require('child_process');
|
||||
const util = require('util');
|
||||
const execPromise = util.promisify(child_process.exec);
|
||||
|
||||
process.env.NODE_ENV = 'test'; // Set the environment to testing
|
||||
const cdata = require('./cdata.js');
|
||||
|
||||
describe('Function', () => {
|
||||
const testFolderPath = path.join(__dirname, 'testFolder');
|
||||
const oldFilePath = path.join(testFolderPath, 'oldFile.txt');
|
||||
const newFilePath = path.join(testFolderPath, 'newFile.txt');
|
||||
|
||||
// Create a temporary file before the test
|
||||
before(() => {
|
||||
// Create test folder
|
||||
if (!fs.existsSync(testFolderPath)) {
|
||||
fs.mkdirSync(testFolderPath);
|
||||
}
|
||||
|
||||
// Create an old file
|
||||
fs.writeFileSync(oldFilePath, 'This is an old file.');
|
||||
// Modify the 'mtime' to simulate an old file
|
||||
const oldTime = new Date();
|
||||
oldTime.setFullYear(oldTime.getFullYear() - 1);
|
||||
fs.utimesSync(oldFilePath, oldTime, oldTime);
|
||||
|
||||
// Create a new file
|
||||
fs.writeFileSync(newFilePath, 'This is a new file.');
|
||||
});
|
||||
|
||||
// delete the temporary files after the test
|
||||
after(() => {
|
||||
fs.rmSync(testFolderPath, { recursive: true });
|
||||
});
|
||||
|
||||
describe('isFileNewerThan', async () => {
|
||||
it('should return true if the file is newer than the provided time', async () => {
|
||||
const pastTime = Date.now() - 10000; // 10 seconds ago
|
||||
assert.strictEqual(cdata.isFileNewerThan(newFilePath, pastTime), true);
|
||||
});
|
||||
|
||||
it('should return false if the file is older than the provided time', async () => {
|
||||
assert.strictEqual(cdata.isFileNewerThan(oldFilePath, Date.now()), false);
|
||||
});
|
||||
|
||||
it('should throw an exception if the file does not exist', async () => {
|
||||
assert.throws(() => {
|
||||
cdata.isFileNewerThan('nonexistent.txt', Date.now());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAnyFileInFolderNewerThan', async () => {
|
||||
it('should return true if a file in the folder is newer than the given time', async () => {
|
||||
const time = fs.statSync(path.join(testFolderPath, 'oldFile.txt')).mtime;
|
||||
assert.strictEqual(cdata.isAnyFileInFolderNewerThan(testFolderPath, time), true);
|
||||
});
|
||||
|
||||
it('should return false if no files in the folder are newer than the given time', async () => {
|
||||
assert.strictEqual(cdata.isAnyFileInFolderNewerThan(testFolderPath, new Date()), false);
|
||||
});
|
||||
|
||||
it('should throw an exception if the folder does not exist', async () => {
|
||||
assert.throws(() => {
|
||||
cdata.isAnyFileInFolderNewerThan('nonexistent', new Date());
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Script', () => {
|
||||
const folderPath = 'wled00';
|
||||
const dataPath = path.join(folderPath, 'data');
|
||||
|
||||
before(() => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
// Backup files
|
||||
fs.cpSync("wled00/data", "wled00Backup", { recursive: true });
|
||||
fs.cpSync("tools/cdata.js", "cdata.bak.js");
|
||||
fs.cpSync("package.json", "package.bak.json");
|
||||
});
|
||||
after(() => {
|
||||
// Restore backup
|
||||
fs.rmSync("wled00/data", { recursive: true });
|
||||
fs.renameSync("wled00Backup", "wled00/data");
|
||||
fs.rmSync("tools/cdata.js");
|
||||
fs.renameSync("cdata.bak.js", "tools/cdata.js");
|
||||
fs.rmSync("package.json");
|
||||
fs.renameSync("package.bak.json", "package.json");
|
||||
});
|
||||
|
||||
// delete all html_*.h files
|
||||
async function deleteBuiltFiles() {
|
||||
const files = await fs.promises.readdir(folderPath);
|
||||
await Promise.all(files.map(file => {
|
||||
if (file.startsWith('html_') && path.extname(file) === '.h') {
|
||||
return fs.promises.unlink(path.join(folderPath, file));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// check if html_*.h files were created
|
||||
async function checkIfBuiltFilesExist() {
|
||||
const files = await fs.promises.readdir(folderPath);
|
||||
const htmlFiles = files.filter(file => file.startsWith('html_') && path.extname(file) === '.h');
|
||||
assert(htmlFiles.length > 0, 'html_*.h files were not created');
|
||||
}
|
||||
|
||||
async function runAndCheckIfBuiltFilesExist() {
|
||||
await execPromise('node tools/cdata.js');
|
||||
await checkIfBuiltFilesExist();
|
||||
}
|
||||
|
||||
async function checkIfFileWasNewlyCreated(file) {
|
||||
const modifiedTime = fs.statSync(file).mtimeMs;
|
||||
assert(Date.now() - modifiedTime < 500, file + ' was not modified');
|
||||
}
|
||||
|
||||
async function testFileModification(sourceFilePath, resultFile) {
|
||||
// run cdata.js to ensure html_*.h files are created
|
||||
await execPromise('node tools/cdata.js');
|
||||
|
||||
// modify file
|
||||
fs.appendFileSync(sourceFilePath, ' ');
|
||||
// delay for 1 second to ensure the modified time is different
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// run script cdata.js again and wait for it to finish
|
||||
await execPromise('node tools/cdata.js');
|
||||
|
||||
await checkIfFileWasNewlyCreated(path.join(folderPath, resultFile));
|
||||
}
|
||||
|
||||
describe('should build if', () => {
|
||||
it('html_*.h files are missing', async () => {
|
||||
await deleteBuiltFiles();
|
||||
await runAndCheckIfBuiltFilesExist();
|
||||
});
|
||||
|
||||
it('only one html_*.h file is missing', async () => {
|
||||
// run script cdata.js and wait for it to finish
|
||||
await execPromise('node tools/cdata.js');
|
||||
|
||||
// delete a random html_*.h file
|
||||
let files = await fs.promises.readdir(folderPath);
|
||||
let htmlFiles = files.filter(file => file.startsWith('html_') && path.extname(file) === '.h');
|
||||
const randomFile = htmlFiles[Math.floor(Math.random() * htmlFiles.length)];
|
||||
await fs.promises.unlink(path.join(folderPath, randomFile));
|
||||
|
||||
await runAndCheckIfBuiltFilesExist();
|
||||
});
|
||||
|
||||
it('script was executed with -f or --force', async () => {
|
||||
await execPromise('node tools/cdata.js');
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
await execPromise('node tools/cdata.js --force');
|
||||
await checkIfFileWasNewlyCreated(path.join(folderPath, 'html_ui.h'));
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
await execPromise('node tools/cdata.js -f');
|
||||
await checkIfFileWasNewlyCreated(path.join(folderPath, 'html_ui.h'));
|
||||
});
|
||||
|
||||
it('a file changes', async () => {
|
||||
await testFileModification(path.join(dataPath, 'index.htm'), 'html_ui.h');
|
||||
});
|
||||
|
||||
it('a inlined file changes', async () => {
|
||||
await testFileModification(path.join(dataPath, 'index.js'), 'html_ui.h');
|
||||
});
|
||||
|
||||
it('a settings file changes', async () => {
|
||||
await testFileModification(path.join(dataPath, 'settings_leds.htm'), 'html_ui.h');
|
||||
});
|
||||
|
||||
it('the favicon changes', async () => {
|
||||
await testFileModification(path.join(dataPath, 'favicon.ico'), 'html_ui.h');
|
||||
});
|
||||
|
||||
it('cdata.js changes', async () => {
|
||||
await testFileModification('tools/cdata.js', 'html_ui.h');
|
||||
});
|
||||
|
||||
it('package.json changes', async () => {
|
||||
await testFileModification('package.json', 'html_ui.h');
|
||||
});
|
||||
});
|
||||
|
||||
describe('should not build if', () => {
|
||||
it('the files are already built', async () => {
|
||||
await deleteBuiltFiles();
|
||||
|
||||
// run script cdata.js and wait for it to finish
|
||||
let startTime = Date.now();
|
||||
await execPromise('node tools/cdata.js');
|
||||
const firstRunTime = Date.now() - startTime;
|
||||
|
||||
// run script cdata.js and wait for it to finish
|
||||
startTime = Date.now();
|
||||
await execPromise('node tools/cdata.js');
|
||||
const secondRunTime = Date.now() - startTime;
|
||||
|
||||
// check if second run was faster than the first (must be at least 2x faster)
|
||||
assert(secondRunTime < firstRunTime / 2, 'html_*.h files were rebuilt');
|
||||
});
|
||||
});
|
||||
});
|
416
tools/cdata.js
Normal file
@ -0,0 +1,416 @@
|
||||
/**
|
||||
* Writes compressed C arrays of data files (web interface)
|
||||
* How to use it?
|
||||
*
|
||||
* 1) Install Node 20+ and npm
|
||||
* 2) npm install
|
||||
* 3) npm run build
|
||||
*
|
||||
* If you change data folder often, you can run it in monitoring mode (it will recompile and update *.h on every file change)
|
||||
*
|
||||
* > npm run dev
|
||||
*
|
||||
* How it works?
|
||||
*
|
||||
* It uses NodeJS packages to inline, minify and GZIP files. See writeHtmlGzipped and writeChunks invocations at the bottom of the page.
|
||||
*/
|
||||
|
||||
const fs = require("node:fs");
|
||||
const path = require("path");
|
||||
const inline = require("web-resource-inliner");
|
||||
const zlib = require("node:zlib");
|
||||
const CleanCSS = require("clean-css");
|
||||
const minifyHtml = require("html-minifier-terser").minify;
|
||||
const packageJson = require("../package.json");
|
||||
|
||||
// Export functions for testing
|
||||
module.exports = { isFileNewerThan, isAnyFileInFolderNewerThan };
|
||||
|
||||
const output = ["wled00/html_ui.h", "wled00/html_pixart.h", "wled00/html_cpal.h", "wled00/html_pxmagic.h", "wled00/html_settings.h", "wled00/html_other.h"]
|
||||
|
||||
// \x1b[34m is blue, \x1b[36m is cyan, \x1b[0m is reset
|
||||
const wledBanner = `
|
||||
\t\x1b[34m ## ## ## ###### ######
|
||||
\t\x1b[34m## ## ## ## ## ## ##
|
||||
\t\x1b[34m## ## ## ## ###### ## ##
|
||||
\t\x1b[34m## ## ## ## ## ## ##
|
||||
\t\x1b[34m ## ## ###### ###### ######
|
||||
\t\t\x1b[36m build script for web UI
|
||||
\x1b[0m`;
|
||||
|
||||
const singleHeader = `/*
|
||||
* Binary array for the Web UI.
|
||||
* gzip is used for smaller size and improved speeds.
|
||||
*
|
||||
* Please see https://kno.wled.ge/advanced/custom-features/#changing-web-ui
|
||||
* to find out how to easily modify the web UI source!
|
||||
*/
|
||||
|
||||
`;
|
||||
|
||||
const multiHeader = `/*
|
||||
* More web UI HTML source arrays.
|
||||
* This file is auto generated, please don't make any changes manually.
|
||||
*
|
||||
* Instead, see https://kno.wled.ge/advanced/custom-features/#changing-web-ui
|
||||
* to find out how to easily modify the web UI source!
|
||||
*/
|
||||
`;
|
||||
|
||||
function hexdump(buffer, isHex = false) {
|
||||
let lines = [];
|
||||
|
||||
for (let i = 0; i < buffer.length; i += (isHex ? 32 : 16)) {
|
||||
var block;
|
||||
let hexArray = [];
|
||||
if (isHex) {
|
||||
block = buffer.slice(i, i + 32)
|
||||
for (let j = 0; j < block.length; j += 2) {
|
||||
hexArray.push("0x" + block.slice(j, j + 2))
|
||||
}
|
||||
} else {
|
||||
block = buffer.slice(i, i + 16); // cut buffer into blocks of 16
|
||||
for (let value of block) {
|
||||
hexArray.push("0x" + value.toString(16).padStart(2, "0"));
|
||||
}
|
||||
}
|
||||
|
||||
let hexString = hexArray.join(", ");
|
||||
let line = ` ${hexString}`;
|
||||
lines.push(line);
|
||||
}
|
||||
|
||||
return lines.join(",\n");
|
||||
}
|
||||
|
||||
function adoptVersionAndRepo(html) {
|
||||
let repoUrl = packageJson.repository ? packageJson.repository.url : undefined;
|
||||
if (repoUrl) {
|
||||
repoUrl = repoUrl.replace(/^git\+/, "");
|
||||
repoUrl = repoUrl.replace(/\.git$/, "");
|
||||
html = html.replaceAll("https://github.com/atuline/WLED", repoUrl);
|
||||
html = html.replaceAll("https://github.com/wled-dev/WLED", repoUrl);
|
||||
}
|
||||
let version = packageJson.version;
|
||||
if (version) {
|
||||
html = html.replaceAll("##VERSION##", version);
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
async function minify(str, type = "plain") {
|
||||
const options = {
|
||||
collapseWhitespace: true,
|
||||
conservativeCollapse: true, // preserve spaces in text
|
||||
collapseBooleanAttributes: true,
|
||||
collapseInlineTagWhitespace: true,
|
||||
minifyCSS: true,
|
||||
minifyJS: true,
|
||||
removeAttributeQuotes: true,
|
||||
removeComments: true,
|
||||
sortAttributes: true,
|
||||
sortClassName: true,
|
||||
};
|
||||
|
||||
if (type == "plain") {
|
||||
return str;
|
||||
} else if (type == "css-minify") {
|
||||
return new CleanCSS({}).minify(str).styles;
|
||||
} else if (type == "js-minify") {
|
||||
let js = await minifyHtml('<script>' + str + '</script>', options);
|
||||
return js.replace(/<[\/]*script>/g, '');
|
||||
} else if (type == "html-minify") {
|
||||
return await minifyHtml(str, options);
|
||||
}
|
||||
|
||||
throw new Error("Unknown filter: " + type);
|
||||
}
|
||||
|
||||
async function writeHtmlGzipped(sourceFile, resultFile, page) {
|
||||
console.info("Reading " + sourceFile);
|
||||
inline.html({
|
||||
fileContent: fs.readFileSync(sourceFile, "utf8"),
|
||||
relativeTo: path.dirname(sourceFile),
|
||||
strict: true,
|
||||
},
|
||||
async function (error, html) {
|
||||
if (error) throw error;
|
||||
|
||||
html = adoptVersionAndRepo(html);
|
||||
const originalLength = html.length;
|
||||
html = await minify(html, "html-minify");
|
||||
const result = zlib.gzipSync(html, { level: zlib.constants.Z_BEST_COMPRESSION });
|
||||
console.info("Minified and compressed " + sourceFile + " from " + originalLength + " to " + result.length + " bytes");
|
||||
const array = hexdump(result);
|
||||
let src = singleHeader;
|
||||
src += `const uint16_t PAGE_${page}_L = ${result.length};\n`;
|
||||
src += `const uint8_t PAGE_${page}[] PROGMEM = {\n${array}\n};\n\n`;
|
||||
console.info("Writing " + resultFile);
|
||||
fs.writeFileSync(resultFile, src);
|
||||
});
|
||||
}
|
||||
|
||||
async function specToChunk(srcDir, s) {
|
||||
const buf = fs.readFileSync(srcDir + "/" + s.file);
|
||||
let chunk = `\n// Autogenerated from ${srcDir}/${s.file}, do not edit!!\n`
|
||||
|
||||
if (s.method == "plaintext" || s.method == "gzip") {
|
||||
let str = buf.toString("utf-8");
|
||||
str = adoptVersionAndRepo(str);
|
||||
const originalLength = str.length;
|
||||
if (s.method == "gzip") {
|
||||
if (s.mangle) str = s.mangle(str);
|
||||
const zip = zlib.gzipSync(await minify(str, s.filter), { level: zlib.constants.Z_BEST_COMPRESSION });
|
||||
console.info("Minified and compressed " + s.file + " from " + originalLength + " to " + zip.length + " bytes");
|
||||
const result = hexdump(zip);
|
||||
chunk += `const uint16_t ${s.name}_length = ${zip.length};\n`;
|
||||
chunk += `const uint8_t ${s.name}[] PROGMEM = {\n${result}\n};\n\n`;
|
||||
return chunk;
|
||||
} else {
|
||||
const minified = await minify(str, s.filter);
|
||||
console.info("Minified " + s.file + " from " + originalLength + " to " + minified.length + " bytes");
|
||||
chunk += `const char ${s.name}[] PROGMEM = R"${s.prepend || ""}${minified}${s.append || ""}";\n\n`;
|
||||
return s.mangle ? s.mangle(chunk) : chunk;
|
||||
}
|
||||
} else if (s.method == "binary") {
|
||||
const result = hexdump(buf);
|
||||
chunk += `const uint16_t ${s.name}_length = ${buf.length};\n`;
|
||||
chunk += `const uint8_t ${s.name}[] PROGMEM = {\n${result}\n};\n\n`;
|
||||
return chunk;
|
||||
}
|
||||
|
||||
throw new Error("Unknown method: " + s.method);
|
||||
}
|
||||
|
||||
async function writeChunks(srcDir, specs, resultFile) {
|
||||
let src = multiHeader;
|
||||
for (const s of specs) {
|
||||
console.info("Reading " + srcDir + "/" + s.file + " as " + s.name);
|
||||
src += await specToChunk(srcDir, s);
|
||||
}
|
||||
console.info("Writing " + src.length + " characters into " + resultFile);
|
||||
fs.writeFileSync(resultFile, src);
|
||||
}
|
||||
|
||||
// Check if a file is newer than a given time
|
||||
function isFileNewerThan(filePath, time) {
|
||||
const stats = fs.statSync(filePath);
|
||||
return stats.mtimeMs > time;
|
||||
}
|
||||
|
||||
// Check if any file in a folder (or its subfolders) is newer than a given time
|
||||
function isAnyFileInFolderNewerThan(folderPath, time) {
|
||||
const files = fs.readdirSync(folderPath, { withFileTypes: true });
|
||||
for (const file of files) {
|
||||
const filePath = path.join(folderPath, file.name);
|
||||
if (isFileNewerThan(filePath, time)) {
|
||||
return true;
|
||||
}
|
||||
if (file.isDirectory() && isAnyFileInFolderNewerThan(filePath, time)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the web UI is already built
|
||||
function isAlreadyBuilt(webUIPath, packageJsonPath = "package.json") {
|
||||
let lastBuildTime = Infinity;
|
||||
|
||||
for (const file of output) {
|
||||
try {
|
||||
lastBuildTime = Math.min(lastBuildTime, fs.statSync(file).mtimeMs);
|
||||
} catch (e) {
|
||||
if (e.code !== 'ENOENT') throw e;
|
||||
console.info("File " + file + " does not exist. Rebuilding...");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !isAnyFileInFolderNewerThan(webUIPath, lastBuildTime) && !isFileNewerThan(packageJsonPath, lastBuildTime) && !isFileNewerThan(__filename, lastBuildTime);
|
||||
}
|
||||
|
||||
// Don't run this script if we're in a test environment
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
return;
|
||||
}
|
||||
|
||||
console.info(wledBanner);
|
||||
|
||||
if (isAlreadyBuilt("wled00/data") && process.argv[2] !== '--force' && process.argv[2] !== '-f') {
|
||||
console.info("Web UI is already built");
|
||||
return;
|
||||
}
|
||||
|
||||
writeHtmlGzipped("wled00/data/index.htm", "wled00/html_ui.h", 'index');
|
||||
writeHtmlGzipped("wled00/data/pixart/pixart.htm", "wled00/html_pixart.h", 'pixart');
|
||||
writeHtmlGzipped("wled00/data/cpal/cpal.htm", "wled00/html_cpal.h", 'cpal');
|
||||
writeHtmlGzipped("wled00/data/pxmagic/pxmagic.htm", "wled00/html_pxmagic.h", 'pxmagic');
|
||||
|
||||
writeChunks(
|
||||
"wled00/data",
|
||||
[
|
||||
{
|
||||
file: "style.css",
|
||||
name: "PAGE_settingsCss",
|
||||
method: "gzip",
|
||||
filter: "css-minify",
|
||||
mangle: (str) =>
|
||||
str
|
||||
.replace("%%", "%")
|
||||
},
|
||||
{
|
||||
file: "common.js",
|
||||
name: "JS_common",
|
||||
method: "gzip",
|
||||
filter: "js-minify",
|
||||
},
|
||||
{
|
||||
file: "settings.htm",
|
||||
name: "PAGE_settings",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "settings_wifi.htm",
|
||||
name: "PAGE_settings_wifi",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "settings_leds.htm",
|
||||
name: "PAGE_settings_leds",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "settings_dmx.htm",
|
||||
name: "PAGE_settings_dmx",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "settings_ui.htm",
|
||||
name: "PAGE_settings_ui",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "settings_sync.htm",
|
||||
name: "PAGE_settings_sync",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "settings_time.htm",
|
||||
name: "PAGE_settings_time",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "settings_sec.htm",
|
||||
name: "PAGE_settings_sec",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "settings_um.htm",
|
||||
name: "PAGE_settings_um",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "settings_2D.htm",
|
||||
name: "PAGE_settings_2D",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "settings_pin.htm",
|
||||
name: "PAGE_settings_pin",
|
||||
method: "gzip",
|
||||
filter: "html-minify"
|
||||
}
|
||||
],
|
||||
"wled00/html_settings.h"
|
||||
);
|
||||
|
||||
writeChunks(
|
||||
"wled00/data",
|
||||
[
|
||||
{
|
||||
file: "usermod.htm",
|
||||
name: "PAGE_usermod",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
mangle: (str) =>
|
||||
str.replace(/fetch\("http\:\/\/.*\/win/gms, 'fetch("/win'),
|
||||
},
|
||||
{
|
||||
file: "msg.htm",
|
||||
name: "PAGE_msg",
|
||||
prepend: "=====(",
|
||||
append: ")=====",
|
||||
method: "plaintext",
|
||||
filter: "html-minify",
|
||||
mangle: (str) => str.replace(/\<h2\>.*\<\/body\>/gms, "<h2>%MSG%</body>"),
|
||||
},
|
||||
{
|
||||
file: "dmxmap.htm",
|
||||
name: "PAGE_dmxmap",
|
||||
prepend: "=====(",
|
||||
append: ")=====",
|
||||
method: "plaintext",
|
||||
filter: "html-minify",
|
||||
mangle: (str) => `
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
${str.replace(/function FM\(\)[ ]?\{/gms, "function FM() {%DMXVARS%\n")}
|
||||
#else
|
||||
const char PAGE_dmxmap[] PROGMEM = R"=====()=====";
|
||||
#endif
|
||||
`,
|
||||
},
|
||||
{
|
||||
file: "update.htm",
|
||||
name: "PAGE_update",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
mangle: (str) =>
|
||||
str
|
||||
.replace(
|
||||
/function GetV().*\<\/script\>/gms,
|
||||
"</script><script src=\"/settings/s.js?p=9\"></script>"
|
||||
)
|
||||
},
|
||||
{
|
||||
file: "welcome.htm",
|
||||
name: "PAGE_welcome",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "liveview.htm",
|
||||
name: "PAGE_liveview",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "liveviewws2D.htm",
|
||||
name: "PAGE_liveviewws2D",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "404.htm",
|
||||
name: "PAGE_404",
|
||||
method: "gzip",
|
||||
filter: "html-minify",
|
||||
},
|
||||
{
|
||||
file: "favicon.ico",
|
||||
name: "favicon",
|
||||
method: "binary",
|
||||
}
|
||||
],
|
||||
"wled00/html_other.h"
|
||||
);
|
232
tools/fps_test.htm
Normal file
@ -0,0 +1,232 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>WLED frame rate test tool</title>
|
||||
<style>
|
||||
body {
|
||||
background-color: #222;
|
||||
color: #fff;
|
||||
font-family: Helvetica, Verdana, sans-serif;
|
||||
}
|
||||
input {
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
}
|
||||
#ip {
|
||||
width: 100px;
|
||||
}
|
||||
#secs {
|
||||
width: 36px;
|
||||
}
|
||||
#csva {
|
||||
position: absolute;
|
||||
top: -100px; /*gtfo*/
|
||||
}
|
||||
button {
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
}
|
||||
table, th, td {
|
||||
border: 1px solid #aaa;
|
||||
border-collapse: collapse;
|
||||
text-align: center;
|
||||
}
|
||||
.red {
|
||||
color: #d20;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
var gotfx = false, running = false;
|
||||
var pos = 0, prev = 0, min = 999, max = 0, fpslist = [], names = [], names_checked = [];
|
||||
var to;
|
||||
function S() {
|
||||
document.getElementById('ip').value = localStorage.getItem('locIpFps');
|
||||
if (document.getElementById('ip').value) req(false);
|
||||
}
|
||||
function loadC() {
|
||||
hide(false);
|
||||
var list = localStorage.getItem('fpsFxSelection');
|
||||
if (!list) return;
|
||||
list = JSON.parse(list);
|
||||
var chks = document.querySelectorAll('.fxcheck');
|
||||
for (let i = 0; i < chks.length; i++) {
|
||||
if (i < list.length) chks[i].checked = list[i];
|
||||
}
|
||||
}
|
||||
function saveC() {
|
||||
var list = [];
|
||||
var chks = document.querySelectorAll('.fxcheck');
|
||||
for (let i = 0; i < chks.length; i++) {
|
||||
list.push(chks[i].checked);
|
||||
}
|
||||
localStorage.setItem('fpsFxSelection', JSON.stringify(list));
|
||||
}
|
||||
function setC(c) {
|
||||
hide(false);
|
||||
var chks = document.querySelectorAll('.fxcheck');
|
||||
for (let i = 0; i < chks.length; i++) {
|
||||
chks[i].checked = (c == 255);
|
||||
}
|
||||
if (c == 1 && chks.length > 100) {
|
||||
chks[1].checked = true; //Blink
|
||||
chks[15].checked = true; //Running
|
||||
chks[16].checked = true; //Saw
|
||||
chks[37].checked = true; //Running 2
|
||||
chks[44].checked = true; //Tetrix
|
||||
chks[63].checked = true; //Pride 2015
|
||||
chks[74].checked = true; //Colortwinkles
|
||||
chks[101].checked = true;//Pacifica
|
||||
}
|
||||
}
|
||||
function hide(h) {
|
||||
var trs = document.querySelectorAll('.trs');
|
||||
var chks = document.querySelectorAll('.fxcheck');
|
||||
for (let i = 0; i < trs.length; i++) {
|
||||
trs[i].style.display = (h && !chks[i].checked) ? "none":"table-row";
|
||||
}
|
||||
}
|
||||
function run(init) {
|
||||
if (init) {
|
||||
running = !running;
|
||||
document.getElementById('runbtn').innerText = running ? 'Stop':'Run';
|
||||
if (running) {pos = 0; prev = -1; min = 999; max = 0; fpslist = []; names_checked = []; hide(true);}
|
||||
clearTimeout(to);
|
||||
if (!running) {req({seg:{fx:0},v:true,stop:true}); return;}
|
||||
}
|
||||
if (!gotfx) {req(false); return;}
|
||||
var chks = document.querySelectorAll('.fxcheck');
|
||||
var fpsb = document.querySelectorAll('.fps');
|
||||
if (prev >= 0) {pos++};
|
||||
if (pos >= chks.length) {run(true); return;} //end
|
||||
while (!chks[pos].checked) {
|
||||
fpsb[pos].innerText = "-";
|
||||
pos++;
|
||||
if (pos >= chks.length) {run(true); return;} //end
|
||||
}
|
||||
names_checked.push(names[pos]);
|
||||
var extra = {};
|
||||
try {
|
||||
extra = JSON.parse(document.getElementById('ej').value);
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
var cmd = {seg:{fx:pos},v:true};
|
||||
Object.assign(cmd, extra);
|
||||
req(cmd);
|
||||
}
|
||||
function req(command) {
|
||||
var ip = document.getElementById('ip').value;
|
||||
if (!ip) {alert("Please enter WLED IP"); return;}
|
||||
if (ip != localStorage.getItem('locIpFps')) localStorage.setItem('locIpFps', document.getElementById('ip').value);
|
||||
var url = command ? `http://${ip}/json/si` : `http://${ip}/json/effects`;
|
||||
var type = command ? 'post':'get';
|
||||
var req = undefined;
|
||||
if (command)
|
||||
{
|
||||
req = JSON.stringify(command);
|
||||
}
|
||||
fetch
|
||||
(url, {
|
||||
method: type,
|
||||
headers: {
|
||||
"Content-type": "application/json; charset=UTF-8"
|
||||
},
|
||||
body: req
|
||||
})
|
||||
.then(res => {
|
||||
if (!res.ok) {
|
||||
alert('Data malfunction');
|
||||
}
|
||||
return res.json();
|
||||
})
|
||||
.then(json => {
|
||||
if (!json) {
|
||||
alert('Empty response'); return;
|
||||
}
|
||||
if (!command) {
|
||||
names = json;
|
||||
var tblc = '';
|
||||
for (let i = 0; i < json.length; i++) {
|
||||
tblc += `<tr class="trs"><td><input type="checkbox" class="fxcheck" /></td><td>${i}</td><td>${json[i]}</td><td class="fps"></td></tr>`
|
||||
}
|
||||
var tbl = `<table>
|
||||
<tr>
|
||||
<th>Test?</th><th>ID</th><th>Effect Name</th><th>FPS</th>
|
||||
</tr>
|
||||
${tblc}
|
||||
</table>`;
|
||||
document.getElementById('tablecon').innerHTML = tbl;
|
||||
setC(1);
|
||||
loadC();
|
||||
gotfx = true;
|
||||
document.getElementById('runbtn').innerText = "Run";
|
||||
} else {
|
||||
if (!json.info) return;
|
||||
document.getElementById('leds').innerText = json.info.leds.count;
|
||||
document.getElementById('seg').innerText = json.state.seg[0].len;
|
||||
document.getElementById('bri').innerText = json.state.bri;
|
||||
if (prev >= 0) {
|
||||
var lastfps = parseInt(json.info.leds.fps); //previous FX
|
||||
if (lastfps < min) min = lastfps;
|
||||
if (lastfps > max) max = lastfps;
|
||||
fpslist.push(lastfps);
|
||||
var sum = 0;
|
||||
for (let i = 0; i < fpslist.length; i++) {
|
||||
sum += fpslist[i];
|
||||
}
|
||||
sum /= fpslist.length;
|
||||
document.getElementById('fps_min').innerText = min;
|
||||
document.getElementById('fps_max').innerText = max;
|
||||
document.getElementById('fps_avg').innerText = Math.round(sum*10)/10;
|
||||
var fpsb = document.querySelectorAll('.fps');
|
||||
fpsb[prev].innerHTML = lastfps;
|
||||
}
|
||||
prev = pos;
|
||||
var delay = parseInt(document.getElementById('secs').value)*1000;
|
||||
delay = Math.min(Math.max(delay, 2000), 15000)
|
||||
if (!command.stop) to = setTimeout(run,delay);
|
||||
}
|
||||
})
|
||||
.catch(function (error) {
|
||||
alert('Comms malfunction');
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
function csv(n) {
|
||||
var txt = "";
|
||||
for (let i = 0; i < fpslist.length; i++) {
|
||||
if (!n) txt += names_checked[i] + ',';
|
||||
txt += fpslist[i]; txt += "\n";
|
||||
}
|
||||
document.getElementById('csva').value = txt;
|
||||
var copyText = document.getElementById('csva');
|
||||
|
||||
copyText.select();
|
||||
copyText.setSelectionRange(0, 999999);
|
||||
document.execCommand("copy");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="S()">
|
||||
<h2>Starship monitoring dashboard</h2>
|
||||
(or rather just a WLED frame rate tester lol)<br><br>
|
||||
IP: <input id="ip" /><br>
|
||||
Time per effect: <input type=number id=secs value=5 max=15 min=2 />s<br>
|
||||
Effects to test:
|
||||
<button type="button" onclick="setC(255)">All</button>
|
||||
<button type="button" onclick="setC(1)">Selection 1</button>
|
||||
<button type="button" onclick="setC(0)">None</button>
|
||||
<button type="button" onclick="loadC()">Get LS</button>
|
||||
<button type="button" class="red" onclick="saveC()">Save to LS</button><br>
|
||||
Extra JSON: <input id="ej" /><br>
|
||||
|
||||
<button type="button" onclick="run(true)" id="runbtn">Fetch FX list</button><br>
|
||||
LEDs: <span id="leds">-</span>, Seg: <span id="seg">-</span>, Bri: <span id="bri">-</span><br>
|
||||
FPS min: <span id="fps_min">-</span>, max: <span id="fps_max">-</span>, avg: <span id="fps_avg">-</span><br><br>
|
||||
<div id="tablecon">
|
||||
</div><br>
|
||||
<button type="button" onclick="csv(false)">Copy csv to clipboard</button>
|
||||
<button type="button" onclick="csv(true)">Copy csv (FPS only)</button>
|
||||
<textarea id=csva></textarea>
|
||||
</body>
|
||||
</html>
|
16
tools/multi-update.cmd
Normal file
@ -0,0 +1,16 @@
|
||||
@echo off
|
||||
SETLOCAL
|
||||
SET FWPATH=c:\path\to\your\WLED\build_output\firmware
|
||||
GOTO ESPS
|
||||
|
||||
:UPDATEONE
|
||||
IF NOT EXIST %FWPATH%\%2 GOTO SKIP
|
||||
ping -w 1000 -n 1 %1 | find "TTL=" || GOTO SKIP
|
||||
ECHO Updating %1
|
||||
curl -s -F "update=@%FWPATH%/%2" %1/update >nul
|
||||
:SKIP
|
||||
GOTO:EOF
|
||||
|
||||
:ESPS
|
||||
call :UPDATEONE 192.168.x.x firmware.bin
|
||||
call :UPDATEONE ....
|
19
tools/multi-update.sh
Normal file
@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
FWPATH=/path/to/your/WLED/build_output/firmware
|
||||
|
||||
update_one() {
|
||||
if [ -f $FWPATH/$2 ]; then
|
||||
ping -c 1 $1 >/dev/null
|
||||
PINGRESULT=$?
|
||||
if [ $PINGRESULT -eq 0 ]; then
|
||||
echo Updating $1
|
||||
curl -s -F "update=@${FWPATH}/$2" $1/update >/dev/null
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
update_one 192.168.x.x firmware.bin
|
||||
update_one 192.168.x.x firmware.bin
|
||||
# ...
|
38
tools/stress_test.sh
Normal file
@ -0,0 +1,38 @@
|
||||
#!/bin/bash
|
||||
# Some web server stress tests
|
||||
#
|
||||
# Perform a large number of parallel requests, stress testing the web server
|
||||
# TODO: some kind of performance metrics
|
||||
|
||||
# Accepts three command line arguments:
|
||||
# - first argument - mandatory - IP or hostname of target server
|
||||
# - second argument - target type (optional)
|
||||
# - third argument - xfer count (for replicated targets) (optional)
|
||||
HOST=$1
|
||||
declare -n TARGET_STR="${2:-JSON_LARGER}_TARGETS"
|
||||
REPLICATE_COUNT=$(("${3:-10}"))
|
||||
|
||||
PARALLEL_MAX=${PARALLEL_MAX:-50}
|
||||
|
||||
CURL_ARGS="--compressed --parallel --parallel-immediate --parallel-max ${PARALLEL_MAX}"
|
||||
CURL_PRINT_RESPONSE_ARGS="-w %{http_code}\n"
|
||||
|
||||
JSON_TARGETS=('json/state' 'json/info' 'json/si', 'json/palettes' 'json/fxdata' 'settings/s.js?p=2')
|
||||
FILE_TARGETS=('' 'iro.js' 'rangetouch.js' 'settings' 'settings/wifi')
|
||||
# Replicate one target many times
|
||||
function replicate() {
|
||||
printf "${1}?%d " $(seq 1 ${REPLICATE_COUNT})
|
||||
}
|
||||
read -a JSON_TINY_TARGETS <<< $(replicate "json/nodes")
|
||||
read -a JSON_SMALL_TARGETS <<< $(replicate "json/info")
|
||||
read -a JSON_LARGE_TARGETS <<< $(replicate "json/si")
|
||||
read -a JSON_LARGER_TARGETS <<< $(replicate "json/fxdata")
|
||||
read -a INDEX_TARGETS <<< $(replicate "")
|
||||
|
||||
# Expand target URLS to full arguments for curl
|
||||
TARGETS=(${TARGET_STR[@]})
|
||||
#echo "${TARGETS[@]}"
|
||||
FULL_TGT_OPTIONS=$(printf "http://${HOST}/%s -o /dev/null " "${TARGETS[@]}")
|
||||
#echo ${FULL_TGT_OPTIONS}
|
||||
|
||||
time curl ${CURL_ARGS} ${FULL_TGT_OPTIONS}
|
46
tools/udp_test.py
Normal file
@ -0,0 +1,46 @@
|
||||
import numpy as np
|
||||
import socket
|
||||
|
||||
class WledRealtimeClient:
|
||||
def __init__(self, wled_controller_ip, num_pixels, udp_port=21324, max_pixels_per_packet=126):
|
||||
self.wled_controller_ip = wled_controller_ip
|
||||
self.num_pixels = num_pixels
|
||||
self.udp_port = udp_port
|
||||
self.max_pixels_per_packet = max_pixels_per_packet
|
||||
self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
self._prev_pixels = np.full((3, self.num_pixels), 253, dtype=np.uint8)
|
||||
self.pixels = np.full((3, self.num_pixels), 1, dtype=np.uint8)
|
||||
|
||||
def update(self):
|
||||
# Truncate values and cast to integer
|
||||
self.pixels = np.clip(self.pixels, 0, 255).astype(np.uint8)
|
||||
p = np.copy(self.pixels)
|
||||
|
||||
idx = np.where(~np.all(p == self._prev_pixels, axis=0))[0]
|
||||
num_pixels = len(idx)
|
||||
n_packets = (num_pixels + self.max_pixels_per_packet - 1) // self.max_pixels_per_packet
|
||||
idx_split = np.array_split(idx, n_packets)
|
||||
|
||||
header = bytes([1, 2]) # WARLS protocol header
|
||||
for packet_indices in idx_split:
|
||||
data = bytearray(header)
|
||||
for i in packet_indices:
|
||||
data.extend([i, *p[:, i]]) # Index and RGB values
|
||||
self._sock.sendto(bytes(data), (self.wled_controller_ip, self.udp_port))
|
||||
|
||||
self._prev_pixels = np.copy(p)
|
||||
|
||||
|
||||
|
||||
################################## LED blink test ##################################
|
||||
if __name__ == "__main__":
|
||||
WLED_CONTROLLER_IP = "192.168.1.153"
|
||||
NUM_PIXELS = 255 # Amount of LEDs on your strip
|
||||
import time
|
||||
wled = WledRealtimeClient(WLED_CONTROLLER_IP, NUM_PIXELS)
|
||||
print('Starting LED blink test')
|
||||
while True:
|
||||
for i in range(NUM_PIXELS):
|
||||
wled.pixels[1, i] = 255 if wled.pixels[1, i] == 0 else 0
|
||||
wled.update()
|
||||
time.sleep(.01)
|
256
usermods/ADS1115_v2/ADS1115_v2.cpp
Normal file
@ -0,0 +1,256 @@
|
||||
#include "wled.h"
|
||||
#include <Adafruit_ADS1X15.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "ChannelSettings.h"
|
||||
|
||||
using namespace ADS1115;
|
||||
|
||||
class ADS1115Usermod : public Usermod {
|
||||
public:
|
||||
void setup() {
|
||||
ads.setGain(GAIN_ONE); // 1x gain +/- 4.096V
|
||||
|
||||
if (!ads.begin()) {
|
||||
Serial.println("Failed to initialize ADS");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!initChannel()) {
|
||||
isInitialized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
startReading();
|
||||
|
||||
isEnabled = true;
|
||||
isInitialized = true;
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (isEnabled && millis() - lastTime > loopInterval) {
|
||||
lastTime = millis();
|
||||
|
||||
// If we don't have new data, skip this iteration.
|
||||
if (!ads.conversionComplete()) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateResult();
|
||||
moveToNextChannel();
|
||||
startReading();
|
||||
}
|
||||
}
|
||||
|
||||
void addToJsonInfo(JsonObject& root)
|
||||
{
|
||||
if (!isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
JsonObject user = root[F("u")];
|
||||
if (user.isNull()) user = root.createNestedObject(F("u"));
|
||||
|
||||
for (uint8_t i = 0; i < channelsCount; i++) {
|
||||
ChannelSettings* settingsPtr = &(channelSettings[i]);
|
||||
|
||||
if (!settingsPtr->isEnabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
JsonArray lightArr = user.createNestedArray(settingsPtr->name); //name
|
||||
float value = round((readings[i] + settingsPtr->offset) * settingsPtr->multiplier, settingsPtr->decimals);
|
||||
lightArr.add(value); //value
|
||||
lightArr.add(" " + settingsPtr->units); //unit
|
||||
}
|
||||
}
|
||||
|
||||
void addToConfig(JsonObject& root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject(F("ADC ADS1115"));
|
||||
|
||||
for (uint8_t i = 0; i < channelsCount; i++) {
|
||||
ChannelSettings* settingsPtr = &(channelSettings[i]);
|
||||
JsonObject channel = top.createNestedObject(settingsPtr->settingName);
|
||||
channel[F("Enabled")] = settingsPtr->isEnabled;
|
||||
channel[F("Name")] = settingsPtr->name;
|
||||
channel[F("Units")] = settingsPtr->units;
|
||||
channel[F("Multiplier")] = settingsPtr->multiplier;
|
||||
channel[F("Offset")] = settingsPtr->offset;
|
||||
channel[F("Decimals")] = settingsPtr->decimals;
|
||||
}
|
||||
|
||||
top[F("Loop Interval")] = loopInterval;
|
||||
}
|
||||
|
||||
bool readFromConfig(JsonObject& root)
|
||||
{
|
||||
JsonObject top = root[F("ADC ADS1115")];
|
||||
|
||||
bool configComplete = !top.isNull();
|
||||
bool hasEnabledChannels = false;
|
||||
|
||||
for (uint8_t i = 0; i < channelsCount && configComplete; i++) {
|
||||
ChannelSettings* settingsPtr = &(channelSettings[i]);
|
||||
JsonObject channel = top[settingsPtr->settingName];
|
||||
|
||||
configComplete &= !channel.isNull();
|
||||
|
||||
configComplete &= getJsonValue(channel[F("Enabled")], settingsPtr->isEnabled);
|
||||
configComplete &= getJsonValue(channel[F("Name")], settingsPtr->name);
|
||||
configComplete &= getJsonValue(channel[F("Units")], settingsPtr->units);
|
||||
configComplete &= getJsonValue(channel[F("Multiplier")], settingsPtr->multiplier);
|
||||
configComplete &= getJsonValue(channel[F("Offset")], settingsPtr->offset);
|
||||
configComplete &= getJsonValue(channel[F("Decimals")], settingsPtr->decimals);
|
||||
|
||||
hasEnabledChannels |= settingsPtr->isEnabled;
|
||||
}
|
||||
|
||||
configComplete &= getJsonValue(top[F("Loop Interval")], loopInterval);
|
||||
|
||||
isEnabled = isInitialized && configComplete && hasEnabledChannels;
|
||||
|
||||
return configComplete;
|
||||
}
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_ADS1115;
|
||||
}
|
||||
|
||||
private:
|
||||
static const uint8_t channelsCount = 8;
|
||||
|
||||
ChannelSettings channelSettings[channelsCount] = {
|
||||
{
|
||||
"Differential reading from AIN0 (P) and AIN1 (N)",
|
||||
false,
|
||||
"Differential AIN0 AIN1",
|
||||
"V",
|
||||
ADS1X15_REG_CONFIG_MUX_DIFF_0_1,
|
||||
1,
|
||||
0,
|
||||
3
|
||||
},
|
||||
{
|
||||
"Differential reading from AIN0 (P) and AIN3 (N)",
|
||||
false,
|
||||
"Differential AIN0 AIN3",
|
||||
"V",
|
||||
ADS1X15_REG_CONFIG_MUX_DIFF_0_3,
|
||||
1,
|
||||
0,
|
||||
3
|
||||
},
|
||||
{
|
||||
"Differential reading from AIN1 (P) and AIN3 (N)",
|
||||
false,
|
||||
"Differential AIN1 AIN3",
|
||||
"V",
|
||||
ADS1X15_REG_CONFIG_MUX_DIFF_1_3,
|
||||
1,
|
||||
0,
|
||||
3
|
||||
},
|
||||
{
|
||||
"Differential reading from AIN2 (P) and AIN3 (N)",
|
||||
false,
|
||||
"Differential AIN2 AIN3",
|
||||
"V",
|
||||
ADS1X15_REG_CONFIG_MUX_DIFF_2_3,
|
||||
1,
|
||||
0,
|
||||
3
|
||||
},
|
||||
{
|
||||
"Single-ended reading from AIN0",
|
||||
false,
|
||||
"Single-ended AIN0",
|
||||
"V",
|
||||
ADS1X15_REG_CONFIG_MUX_SINGLE_0,
|
||||
1,
|
||||
0,
|
||||
3
|
||||
},
|
||||
{
|
||||
"Single-ended reading from AIN1",
|
||||
false,
|
||||
"Single-ended AIN1",
|
||||
"V",
|
||||
ADS1X15_REG_CONFIG_MUX_SINGLE_1,
|
||||
1,
|
||||
0,
|
||||
3
|
||||
},
|
||||
{
|
||||
"Single-ended reading from AIN2",
|
||||
false,
|
||||
"Single-ended AIN2",
|
||||
"V",
|
||||
ADS1X15_REG_CONFIG_MUX_SINGLE_2,
|
||||
1,
|
||||
0,
|
||||
3
|
||||
},
|
||||
{
|
||||
"Single-ended reading from AIN3",
|
||||
false,
|
||||
"Single-ended AIN3",
|
||||
"V",
|
||||
ADS1X15_REG_CONFIG_MUX_SINGLE_3,
|
||||
1,
|
||||
0,
|
||||
3
|
||||
},
|
||||
};
|
||||
float readings[channelsCount] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
unsigned long loopInterval = 1000;
|
||||
unsigned long lastTime = 0;
|
||||
|
||||
Adafruit_ADS1115 ads;
|
||||
uint8_t activeChannel;
|
||||
|
||||
bool isEnabled = false;
|
||||
bool isInitialized = false;
|
||||
|
||||
static float round(float value, uint8_t decimals) {
|
||||
return roundf(value * powf(10, decimals)) / powf(10, decimals);
|
||||
}
|
||||
|
||||
bool initChannel() {
|
||||
for (uint8_t i = 0; i < channelsCount; i++) {
|
||||
if (channelSettings[i].isEnabled) {
|
||||
activeChannel = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
activeChannel = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
void moveToNextChannel() {
|
||||
uint8_t oldActiveChannel = activeChannel;
|
||||
|
||||
do
|
||||
{
|
||||
if (++activeChannel >= channelsCount){
|
||||
activeChannel = 0;
|
||||
}
|
||||
}
|
||||
while (!channelSettings[activeChannel].isEnabled && oldActiveChannel != activeChannel);
|
||||
}
|
||||
|
||||
void startReading() {
|
||||
ads.startADCReading(channelSettings[activeChannel].mux, /*continuous=*/false);
|
||||
}
|
||||
|
||||
void updateResult() {
|
||||
int16_t results = ads.getLastConversionResults();
|
||||
readings[activeChannel] = ads.computeVolts(results);
|
||||
}
|
||||
};
|
||||
|
||||
static ADS1115Usermod ads1115_v2;
|
||||
REGISTER_USERMOD(ads1115_v2);
|
15
usermods/ADS1115_v2/ChannelSettings.h
Normal file
@ -0,0 +1,15 @@
|
||||
#include "wled.h"
|
||||
|
||||
namespace ADS1115
|
||||
{
|
||||
struct ChannelSettings {
|
||||
const String settingName;
|
||||
bool isEnabled;
|
||||
String name;
|
||||
String units;
|
||||
const uint16_t mux;
|
||||
float multiplier;
|
||||
float offset;
|
||||
uint8_t decimals;
|
||||
};
|
||||
}
|
7
usermods/ADS1115_v2/library.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"name:": "ADS1115_v2",
|
||||
"dependencies": {
|
||||
"Adafruit BusIO": "https://github.com/adafruit/Adafruit_BusIO#1.13.2",
|
||||
"Adafruit ADS1X15": "https://github.com/adafruit/Adafruit_ADS1X15#2.4.0"
|
||||
}
|
||||
}
|
10
usermods/ADS1115_v2/readme.md
Normal file
@ -0,0 +1,10 @@
|
||||
# ADS1115 16-Bit ADC with four inputs
|
||||
|
||||
This usermod will read from an ADS1115 ADC. The voltages are displayed in the Info section of the web UI.
|
||||
|
||||
Configuration is performed via the Usermod menu. There are no parameters to set in code!
|
||||
|
||||
## Installation
|
||||
|
||||
Add 'ADS1115' to `custom_usermods` in your platformio environment.
|
||||
|
328
usermods/AHT10_v2/AHT10_v2.cpp
Normal file
@ -0,0 +1,328 @@
|
||||
#include "wled.h"
|
||||
#include <AHT10.h>
|
||||
|
||||
#define AHT10_SUCCESS 1
|
||||
|
||||
class UsermodAHT10 : public Usermod
|
||||
{
|
||||
private:
|
||||
static const char _name[];
|
||||
|
||||
unsigned long _lastLoopCheck = 0;
|
||||
|
||||
bool _settingEnabled : 1; // Enable the usermod
|
||||
bool _mqttPublish : 1; // Publish mqtt values
|
||||
bool _mqttPublishAlways : 1; // Publish always, regardless if there is a change
|
||||
bool _mqttHomeAssistant : 1; // Enable Home Assistant docs
|
||||
bool _initDone : 1; // Initialization is done
|
||||
|
||||
// Settings. Some of these are stored in a different format than they're user settings - so we don't have to convert at runtime
|
||||
uint8_t _i2cAddress = AHT10_ADDRESS_0X38;
|
||||
ASAIR_I2C_SENSOR _ahtType = AHT10_SENSOR;
|
||||
uint16_t _checkInterval = 60000; // milliseconds, user settings is in seconds
|
||||
float _decimalFactor = 100; // a power of 10 factor. 1 would be no change, 10 is one decimal, 100 is two etc. User sees a power of 10 (0, 1, 2, ..)
|
||||
|
||||
uint8_t _lastStatus = 0;
|
||||
float _lastHumidity = 0;
|
||||
float _lastTemperature = 0;
|
||||
|
||||
#ifndef WLED_MQTT_DISABLE
|
||||
float _lastHumiditySent = 0;
|
||||
float _lastTemperatureSent = 0;
|
||||
#endif
|
||||
|
||||
AHT10 *_aht = nullptr;
|
||||
|
||||
float truncateDecimals(float val)
|
||||
{
|
||||
return roundf(val * _decimalFactor) / _decimalFactor;
|
||||
}
|
||||
|
||||
void initializeAht()
|
||||
{
|
||||
if (_aht != nullptr)
|
||||
{
|
||||
delete _aht;
|
||||
}
|
||||
|
||||
_aht = new AHT10(_i2cAddress, _ahtType);
|
||||
|
||||
_lastStatus = 0;
|
||||
_lastHumidity = 0;
|
||||
_lastTemperature = 0;
|
||||
}
|
||||
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
void mqttInitialize()
|
||||
{
|
||||
// This is a generic "setup mqtt" function, So we must abort if we're not to do mqtt
|
||||
if (!WLED_MQTT_CONNECTED || !_mqttPublish || !_mqttHomeAssistant)
|
||||
return;
|
||||
|
||||
char topic[128];
|
||||
snprintf_P(topic, 127, "%s/temperature", mqttDeviceTopic);
|
||||
mqttCreateHassSensor(F("Temperature"), topic, F("temperature"), F("°C"));
|
||||
|
||||
snprintf_P(topic, 127, "%s/humidity", mqttDeviceTopic);
|
||||
mqttCreateHassSensor(F("Humidity"), topic, F("humidity"), F("%"));
|
||||
}
|
||||
|
||||
void mqttPublishIfChanged(const __FlashStringHelper *topic, float &lastState, float state, float minChange)
|
||||
{
|
||||
// Check if MQTT Connected, otherwise it will crash the 8266
|
||||
// Only report if the change is larger than the required diff
|
||||
if (WLED_MQTT_CONNECTED && _mqttPublish && (_mqttPublishAlways || fabsf(lastState - state) > minChange))
|
||||
{
|
||||
char subuf[128];
|
||||
snprintf_P(subuf, 127, PSTR("%s/%s"), mqttDeviceTopic, (const char *)topic);
|
||||
mqtt->publish(subuf, 0, false, String(state).c_str());
|
||||
|
||||
lastState = state;
|
||||
}
|
||||
}
|
||||
|
||||
// Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop.
|
||||
void mqttCreateHassSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement)
|
||||
{
|
||||
String t = String(F("homeassistant/sensor/")) + mqttClientID + "/" + name + F("/config");
|
||||
|
||||
StaticJsonDocument<600> doc;
|
||||
|
||||
doc[F("name")] = name;
|
||||
doc[F("state_topic")] = topic;
|
||||
doc[F("unique_id")] = String(mqttClientID) + name;
|
||||
if (unitOfMeasurement != "")
|
||||
doc[F("unit_of_measurement")] = unitOfMeasurement;
|
||||
if (deviceClass != "")
|
||||
doc[F("device_class")] = deviceClass;
|
||||
doc[F("expire_after")] = 1800;
|
||||
|
||||
JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device
|
||||
device[F("name")] = serverDescription;
|
||||
device[F("identifiers")] = "wled-sensor-" + String(mqttClientID);
|
||||
device[F("manufacturer")] = F(WLED_BRAND);
|
||||
device[F("model")] = F(WLED_PRODUCT_NAME);
|
||||
device[F("sw_version")] = versionString;
|
||||
|
||||
String temp;
|
||||
serializeJson(doc, temp);
|
||||
DEBUG_PRINTLN(t);
|
||||
DEBUG_PRINTLN(temp);
|
||||
|
||||
mqtt->publish(t.c_str(), 0, true, temp.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
public:
|
||||
void setup()
|
||||
{
|
||||
initializeAht();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// if usermod is disabled or called during strip updating just exit
|
||||
// NOTE: on very long strips strip.isUpdating() may always return true so update accordingly
|
||||
if (!_settingEnabled || strip.isUpdating())
|
||||
return;
|
||||
|
||||
// do your magic here
|
||||
unsigned long currentTime = millis();
|
||||
|
||||
if (currentTime - _lastLoopCheck < _checkInterval)
|
||||
return;
|
||||
_lastLoopCheck = currentTime;
|
||||
|
||||
_lastStatus = _aht->readRawData();
|
||||
|
||||
if (_lastStatus == AHT10_ERROR)
|
||||
{
|
||||
// Perform softReset and retry
|
||||
DEBUG_PRINTLN(F("AHTxx returned error, doing softReset"));
|
||||
if (!_aht->softReset())
|
||||
{
|
||||
DEBUG_PRINTLN(F("softReset failed"));
|
||||
return;
|
||||
}
|
||||
|
||||
_lastStatus = _aht->readRawData();
|
||||
}
|
||||
|
||||
if (_lastStatus == AHT10_SUCCESS)
|
||||
{
|
||||
float temperature = truncateDecimals(_aht->readTemperature(AHT10_USE_READ_DATA));
|
||||
float humidity = truncateDecimals(_aht->readHumidity(AHT10_USE_READ_DATA));
|
||||
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
// Push to MQTT
|
||||
|
||||
// We can avoid reporting if the change is insignificant. The threshold chosen is below the level of accuracy, but way above 0.01 which is the precision of the value provided.
|
||||
// The AHT10/15/20 has an accuracy of 0.3C in the temperature readings
|
||||
mqttPublishIfChanged(F("temperature"), _lastTemperatureSent, temperature, 0.1f);
|
||||
|
||||
// The AHT10/15/20 has an accuracy in the humidity sensor of 2%
|
||||
mqttPublishIfChanged(F("humidity"), _lastHumiditySent, humidity, 0.5f);
|
||||
#endif
|
||||
|
||||
// Store
|
||||
_lastTemperature = temperature;
|
||||
_lastHumidity = humidity;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
void onMqttConnect(bool sessionPresent)
|
||||
{
|
||||
mqttInitialize();
|
||||
}
|
||||
#endif
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_AHT10;
|
||||
}
|
||||
|
||||
void addToJsonInfo(JsonObject &root) override
|
||||
{
|
||||
// if "u" object does not exist yet wee need to create it
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull())
|
||||
user = root.createNestedObject("u");
|
||||
|
||||
#ifdef USERMOD_AHT10_DEBUG
|
||||
JsonArray temp = user.createNestedArray(F("AHT last loop"));
|
||||
temp.add(_lastLoopCheck);
|
||||
|
||||
temp = user.createNestedArray(F("AHT last status"));
|
||||
temp.add(_lastStatus);
|
||||
#endif
|
||||
|
||||
JsonArray jsonTemp = user.createNestedArray(F("Temperature"));
|
||||
JsonArray jsonHumidity = user.createNestedArray(F("Humidity"));
|
||||
|
||||
if (_lastLoopCheck == 0)
|
||||
{
|
||||
// Before first run
|
||||
jsonTemp.add(F("Not read yet"));
|
||||
jsonHumidity.add(F("Not read yet"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (_lastStatus != AHT10_SUCCESS)
|
||||
{
|
||||
jsonTemp.add(F("An error occurred"));
|
||||
jsonHumidity.add(F("An error occurred"));
|
||||
return;
|
||||
}
|
||||
|
||||
jsonTemp.add(_lastTemperature);
|
||||
jsonTemp.add(F("°C"));
|
||||
|
||||
jsonHumidity.add(_lastHumidity);
|
||||
jsonHumidity.add(F("%"));
|
||||
}
|
||||
|
||||
void addToConfig(JsonObject &root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name));
|
||||
top[F("Enabled")] = _settingEnabled;
|
||||
top[F("I2CAddress")] = static_cast<uint8_t>(_i2cAddress);
|
||||
top[F("SensorType")] = _ahtType;
|
||||
top[F("CheckInterval")] = _checkInterval / 1000;
|
||||
top[F("Decimals")] = log10f(_decimalFactor);
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
top[F("MqttPublish")] = _mqttPublish;
|
||||
top[F("MqttPublishAlways")] = _mqttPublishAlways;
|
||||
top[F("MqttHomeAssistantDiscovery")] = _mqttHomeAssistant;
|
||||
#endif
|
||||
|
||||
DEBUG_PRINTLN(F("AHT10 config saved."));
|
||||
}
|
||||
|
||||
bool readFromConfig(JsonObject &root) override
|
||||
{
|
||||
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor
|
||||
// setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
|
||||
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
|
||||
bool configComplete = !top.isNull();
|
||||
if (!configComplete)
|
||||
return false;
|
||||
|
||||
bool tmpBool = false;
|
||||
configComplete &= getJsonValue(top[F("Enabled")], tmpBool);
|
||||
if (configComplete)
|
||||
_settingEnabled = tmpBool;
|
||||
|
||||
configComplete &= getJsonValue(top[F("I2CAddress")], _i2cAddress);
|
||||
configComplete &= getJsonValue(top[F("CheckInterval")], _checkInterval);
|
||||
if (configComplete)
|
||||
{
|
||||
if (1 <= _checkInterval && _checkInterval <= 600)
|
||||
_checkInterval *= 1000;
|
||||
else
|
||||
// Invalid input
|
||||
_checkInterval = 60000;
|
||||
}
|
||||
|
||||
configComplete &= getJsonValue(top[F("Decimals")], _decimalFactor);
|
||||
if (configComplete)
|
||||
{
|
||||
if (0 <= _decimalFactor && _decimalFactor <= 5)
|
||||
_decimalFactor = pow10f(_decimalFactor);
|
||||
else
|
||||
// Invalid input
|
||||
_decimalFactor = 100;
|
||||
}
|
||||
|
||||
uint8_t tmpAhtType;
|
||||
configComplete &= getJsonValue(top[F("SensorType")], tmpAhtType);
|
||||
if (configComplete)
|
||||
{
|
||||
if (0 <= tmpAhtType && tmpAhtType <= 2)
|
||||
_ahtType = static_cast<ASAIR_I2C_SENSOR>(tmpAhtType);
|
||||
else
|
||||
// Invalid input
|
||||
_ahtType = ASAIR_I2C_SENSOR::AHT10_SENSOR;
|
||||
}
|
||||
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
configComplete &= getJsonValue(top[F("MqttPublish")], tmpBool);
|
||||
if (configComplete)
|
||||
_mqttPublish = tmpBool;
|
||||
|
||||
configComplete &= getJsonValue(top[F("MqttPublishAlways")], tmpBool);
|
||||
if (configComplete)
|
||||
_mqttPublishAlways = tmpBool;
|
||||
|
||||
configComplete &= getJsonValue(top[F("MqttHomeAssistantDiscovery")], tmpBool);
|
||||
if (configComplete)
|
||||
_mqttHomeAssistant = tmpBool;
|
||||
#endif
|
||||
|
||||
if (_initDone)
|
||||
{
|
||||
// Reloading config
|
||||
initializeAht();
|
||||
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
mqttInitialize();
|
||||
#endif
|
||||
}
|
||||
|
||||
_initDone = true;
|
||||
return configComplete;
|
||||
}
|
||||
|
||||
~UsermodAHT10()
|
||||
{
|
||||
delete _aht;
|
||||
_aht = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
const char UsermodAHT10::_name[] PROGMEM = "AHTxx";
|
||||
|
||||
static UsermodAHT10 aht10_v2;
|
||||
REGISTER_USERMOD(aht10_v2);
|
30
usermods/AHT10_v2/README.md
Normal file
@ -0,0 +1,30 @@
|
||||
# Usermod AHT10
|
||||
This Usermod is designed to read a `AHT10`, `AHT15` or `AHT20` sensor and output the following:
|
||||
- Temperature
|
||||
- Humidity
|
||||
|
||||
Configuration is performed via the Usermod menu. The following settings can be configured in the Usermod Menu:
|
||||
- I2CAddress: The i2c address in decimal. Set it to either 56 (0x38, the default) or 57 (0x39).
|
||||
- SensorType, one of:
|
||||
- 0 - AHT10
|
||||
- 1 - AHT15
|
||||
- 2 - AHT20
|
||||
- CheckInterval: Number of seconds between readings
|
||||
- Decimals: Number of decimals to put in the output
|
||||
|
||||
Dependencies, These must be added under `lib_deps` in your `platform.ini` (or `platform_override.ini`).
|
||||
- Libraries
|
||||
- `enjoyneering/AHT10@~1.1.0` (by [enjoyneering](https://registry.platformio.org/libraries/enjoyneering/AHT10))
|
||||
- `Wire`
|
||||
|
||||
## Author
|
||||
[@LordMike](https://github.com/LordMike)
|
||||
|
||||
# Compiling
|
||||
|
||||
To enable, add 'AHT10' to `custom_usermods` in your platformio encrionment (e.g. in `platformio_override.ini`)
|
||||
```ini
|
||||
[env:aht10_example]
|
||||
extends = env:esp32dev
|
||||
custom_usermods = ${env:esp32dev.custom_usermods} AHT10
|
||||
```
|
6
usermods/AHT10_v2/library.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name:": "AHT10_v2",
|
||||
"dependencies": {
|
||||
"enjoyneering/AHT10":"~1.1.0"
|
||||
}
|
||||
}
|
5
usermods/AHT10_v2/platformio_override.ini
Normal file
@ -0,0 +1,5 @@
|
||||
[env:aht10_example]
|
||||
extends = env:esp32dev
|
||||
build_flags =
|
||||
${common.build_flags} ${esp32.build_flags}
|
||||
; -D USERMOD_AHT10_DEBUG ; -- add a debug status to the info modal
|
259
usermods/Analog_Clock/Analog_Clock.cpp
Normal file
@ -0,0 +1,259 @@
|
||||
#include "wled.h"
|
||||
|
||||
/*
|
||||
* Usermod for analog clock
|
||||
*/
|
||||
extern Timezone* tz;
|
||||
|
||||
class AnalogClockUsermod : public Usermod {
|
||||
private:
|
||||
static constexpr uint32_t refreshRate = 50; // per second
|
||||
static constexpr uint32_t refreshDelay = 1000 / refreshRate;
|
||||
|
||||
struct Segment {
|
||||
// config
|
||||
int16_t firstLed = 0;
|
||||
int16_t lastLed = 59;
|
||||
int16_t centerLed = 0;
|
||||
|
||||
// runtime
|
||||
int16_t size;
|
||||
|
||||
Segment() {
|
||||
update();
|
||||
}
|
||||
|
||||
void validateAndUpdate() {
|
||||
if (firstLed < 0 || firstLed >= strip.getLengthTotal() ||
|
||||
lastLed < firstLed || lastLed >= strip.getLengthTotal()) {
|
||||
*this = {};
|
||||
return;
|
||||
}
|
||||
if (centerLed < firstLed || centerLed > lastLed) {
|
||||
centerLed = firstLed;
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void update() {
|
||||
size = lastLed - firstLed + 1;
|
||||
}
|
||||
};
|
||||
|
||||
// configuration (available in API and stored in flash)
|
||||
bool enabled = false;
|
||||
Segment mainSegment;
|
||||
bool hourMarksEnabled = true;
|
||||
uint32_t hourMarkColor = 0xFF0000;
|
||||
uint32_t hourColor = 0x0000FF;
|
||||
uint32_t minuteColor = 0x00FF00;
|
||||
bool secondsEnabled = true;
|
||||
Segment secondsSegment;
|
||||
uint32_t secondColor = 0xFF0000;
|
||||
bool blendColors = true;
|
||||
uint16_t secondsEffect = 0;
|
||||
|
||||
// runtime
|
||||
bool initDone = false;
|
||||
uint32_t lastOverlayDraw = 0;
|
||||
|
||||
void validateAndUpdate() {
|
||||
mainSegment.validateAndUpdate();
|
||||
secondsSegment.validateAndUpdate();
|
||||
if (secondsEffect < 0 || secondsEffect > 1) {
|
||||
secondsEffect = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int16_t adjustToSegment(double progress, Segment const& segment) {
|
||||
int16_t led = segment.centerLed + progress * segment.size;
|
||||
return led > segment.lastLed
|
||||
? segment.firstLed + led - segment.lastLed - 1
|
||||
: led;
|
||||
}
|
||||
|
||||
void setPixelColor(uint16_t n, uint32_t c) {
|
||||
if (!blendColors) {
|
||||
strip.setPixelColor(n, c);
|
||||
} else {
|
||||
uint32_t oldC = strip.getPixelColor(n);
|
||||
strip.setPixelColor(n, qadd32(oldC, c));
|
||||
}
|
||||
}
|
||||
|
||||
String colorToHexString(uint32_t c) {
|
||||
char buffer[9];
|
||||
sprintf(buffer, "%06X", c);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool hexStringToColor(String const& s, uint32_t& c, uint32_t def) {
|
||||
char *ep;
|
||||
unsigned long long r = strtoull(s.c_str(), &ep, 16);
|
||||
if (*ep == 0) {
|
||||
c = r;
|
||||
return true;
|
||||
} else {
|
||||
c = def;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void secondsEffectSineFade(int16_t secondLed, Toki::Time const& time) {
|
||||
uint32_t ms = time.ms % 1000;
|
||||
uint8_t b0 = (cos8_t(ms * 64 / 1000) - 128) * 2;
|
||||
setPixelColor(secondLed, gamma32(scale32(secondColor, b0)));
|
||||
uint8_t b1 = (sin8_t(ms * 64 / 1000) - 128) * 2;
|
||||
setPixelColor(inc(secondLed, 1, secondsSegment), gamma32(scale32(secondColor, b1)));
|
||||
}
|
||||
|
||||
static inline uint32_t qadd32(uint32_t c1, uint32_t c2) {
|
||||
return RGBW32(
|
||||
qadd8(R(c1), R(c2)),
|
||||
qadd8(G(c1), G(c2)),
|
||||
qadd8(B(c1), B(c2)),
|
||||
qadd8(W(c1), W(c2))
|
||||
);
|
||||
}
|
||||
|
||||
static inline uint32_t scale32(uint32_t c, fract8 scale) {
|
||||
return RGBW32(
|
||||
scale8(R(c), scale),
|
||||
scale8(G(c), scale),
|
||||
scale8(B(c), scale),
|
||||
scale8(W(c), scale)
|
||||
);
|
||||
}
|
||||
|
||||
static inline int16_t dec(int16_t n, int16_t i, Segment const& seg) {
|
||||
return n - seg.firstLed >= i
|
||||
? n - i
|
||||
: seg.lastLed - seg.firstLed - i + n + 1;
|
||||
}
|
||||
|
||||
static inline int16_t inc(int16_t n, int16_t i, Segment const& seg) {
|
||||
int16_t r = n + i;
|
||||
if (r > seg.lastLed) {
|
||||
return seg.firstLed + n - seg.lastLed;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
public:
|
||||
AnalogClockUsermod() {
|
||||
}
|
||||
|
||||
void setup() override {
|
||||
initDone = true;
|
||||
validateAndUpdate();
|
||||
}
|
||||
|
||||
void loop() override {
|
||||
if (millis() - lastOverlayDraw > refreshDelay) {
|
||||
strip.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
void handleOverlayDraw() override {
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
lastOverlayDraw = millis();
|
||||
|
||||
auto time = toki.getTime();
|
||||
double secondP = second(localTime) / 60.0;
|
||||
double minuteP = minute(localTime) / 60.0;
|
||||
double hourP = (hour(localTime) % 12) / 12.0 + minuteP / 12.0;
|
||||
|
||||
if (hourMarksEnabled) {
|
||||
for (int Led = 0; Led <= 55; Led = Led + 5)
|
||||
{
|
||||
int16_t hourmarkled = adjustToSegment(Led / 60.0, mainSegment);
|
||||
setPixelColor(hourmarkled, hourMarkColor);
|
||||
}
|
||||
}
|
||||
|
||||
if (secondsEnabled) {
|
||||
int16_t secondLed = adjustToSegment(secondP, secondsSegment);
|
||||
|
||||
switch (secondsEffect) {
|
||||
case 0: // no effect
|
||||
setPixelColor(secondLed, secondColor);
|
||||
break;
|
||||
|
||||
case 1: // fading seconds
|
||||
secondsEffectSineFade(secondLed, time);
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: move to secondsTrailEffect
|
||||
// for (uint16_t i = 1; i < secondsTrail + 1; ++i) {
|
||||
// uint16_t trailLed = dec(secondLed, i, secondsSegment);
|
||||
// uint8_t trailBright = 255 / (secondsTrail + 1) * (secondsTrail - i + 1);
|
||||
// setPixelColor(trailLed, gamma32(scale32(secondColor, trailBright)));
|
||||
// }
|
||||
}
|
||||
|
||||
setPixelColor(adjustToSegment(minuteP, mainSegment), minuteColor);
|
||||
setPixelColor(adjustToSegment(hourP, mainSegment), hourColor);
|
||||
}
|
||||
|
||||
void addToConfig(JsonObject& root) override {
|
||||
validateAndUpdate();
|
||||
|
||||
JsonObject top = root.createNestedObject(F("Analog Clock"));
|
||||
top[F("Overlay Enabled")] = enabled;
|
||||
top[F("First LED (Main Ring)")] = mainSegment.firstLed;
|
||||
top[F("Last LED (Main Ring)")] = mainSegment.lastLed;
|
||||
top[F("Center/12h LED (Main Ring)")] = mainSegment.centerLed;
|
||||
top[F("Hour Marks Enabled")] = hourMarksEnabled;
|
||||
top[F("Hour Mark Color (RRGGBB)")] = colorToHexString(hourMarkColor);
|
||||
top[F("Hour Color (RRGGBB)")] = colorToHexString(hourColor);
|
||||
top[F("Minute Color (RRGGBB)")] = colorToHexString(minuteColor);
|
||||
top[F("Show Seconds")] = secondsEnabled;
|
||||
top[F("First LED (Seconds Ring)")] = secondsSegment.firstLed;
|
||||
top[F("Last LED (Seconds Ring)")] = secondsSegment.lastLed;
|
||||
top[F("Center/12h LED (Seconds Ring)")] = secondsSegment.centerLed;
|
||||
top[F("Second Color (RRGGBB)")] = colorToHexString(secondColor);
|
||||
top[F("Seconds Effect (0-1)")] = secondsEffect;
|
||||
top[F("Blend Colors")] = blendColors;
|
||||
}
|
||||
|
||||
bool readFromConfig(JsonObject& root) override {
|
||||
JsonObject top = root[F("Analog Clock")];
|
||||
|
||||
bool configComplete = !top.isNull();
|
||||
|
||||
String color;
|
||||
configComplete &= getJsonValue(top[F("Overlay Enabled")], enabled, false);
|
||||
configComplete &= getJsonValue(top[F("First LED (Main Ring)")], mainSegment.firstLed, 0);
|
||||
configComplete &= getJsonValue(top[F("Last LED (Main Ring)")], mainSegment.lastLed, 59);
|
||||
configComplete &= getJsonValue(top[F("Center/12h LED (Main Ring)")], mainSegment.centerLed, 0);
|
||||
configComplete &= getJsonValue(top[F("Hour Marks Enabled")], hourMarksEnabled, false);
|
||||
configComplete &= getJsonValue(top[F("Hour Mark Color (RRGGBB)")], color, F("161616")) && hexStringToColor(color, hourMarkColor, 0x161616);
|
||||
configComplete &= getJsonValue(top[F("Hour Color (RRGGBB)")], color, F("0000FF")) && hexStringToColor(color, hourColor, 0x0000FF);
|
||||
configComplete &= getJsonValue(top[F("Minute Color (RRGGBB)")], color, F("00FF00")) && hexStringToColor(color, minuteColor, 0x00FF00);
|
||||
configComplete &= getJsonValue(top[F("Show Seconds")], secondsEnabled, true);
|
||||
configComplete &= getJsonValue(top[F("First LED (Seconds Ring)")], secondsSegment.firstLed, 0);
|
||||
configComplete &= getJsonValue(top[F("Last LED (Seconds Ring)")], secondsSegment.lastLed, 59);
|
||||
configComplete &= getJsonValue(top[F("Center/12h LED (Seconds Ring)")], secondsSegment.centerLed, 0);
|
||||
configComplete &= getJsonValue(top[F("Second Color (RRGGBB)")], color, F("FF0000")) && hexStringToColor(color, secondColor, 0xFF0000);
|
||||
configComplete &= getJsonValue(top[F("Seconds Effect (0-1)")], secondsEffect, 0);
|
||||
configComplete &= getJsonValue(top[F("Blend Colors")], blendColors, true);
|
||||
|
||||
if (initDone) {
|
||||
validateAndUpdate();
|
||||
}
|
||||
|
||||
return configComplete;
|
||||
}
|
||||
|
||||
uint16_t getId() override {
|
||||
return USERMOD_ID_ANALOG_CLOCK;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static AnalogClockUsermod analog_clock;
|
||||
REGISTER_USERMOD(analog_clock);
|
3
usermods/Analog_Clock/library.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"name:": "Analog_Clock"
|
||||
}
|
567
usermods/Animated_Staircase/Animated_Staircase.cpp
Normal file
@ -0,0 +1,567 @@
|
||||
/*
|
||||
* Usermod for detecting people entering/leaving a staircase and switching the
|
||||
* staircase on/off.
|
||||
*
|
||||
* Edit the Animated_Staircase_config.h file to compile this usermod for your
|
||||
* specific configuration.
|
||||
*
|
||||
* See the accompanying README.md file for more info.
|
||||
*/
|
||||
#include "wled.h"
|
||||
|
||||
class Animated_Staircase : public Usermod {
|
||||
private:
|
||||
|
||||
/* configuration (available in API and stored in flash) */
|
||||
bool enabled = false; // Enable this usermod
|
||||
unsigned long segment_delay_ms = 150; // Time between switching each segment
|
||||
unsigned long on_time_ms = 30000; // The time for the light to stay on
|
||||
int8_t topPIRorTriggerPin = -1; // disabled
|
||||
int8_t bottomPIRorTriggerPin = -1; // disabled
|
||||
int8_t topEchoPin = -1; // disabled
|
||||
int8_t bottomEchoPin = -1; // disabled
|
||||
bool useUSSensorTop = false; // using PIR or UltraSound sensor?
|
||||
bool useUSSensorBottom = false; // using PIR or UltraSound sensor?
|
||||
unsigned int topMaxDist = 50; // default maximum measured distance in cm, top
|
||||
unsigned int bottomMaxDist = 50; // default maximum measured distance in cm, bottom
|
||||
bool togglePower = false; // toggle power on/off with staircase on/off
|
||||
|
||||
/* runtime variables */
|
||||
bool initDone = false;
|
||||
|
||||
// Time between checking of the sensors
|
||||
const unsigned int scanDelay = 100;
|
||||
|
||||
// Lights on or off.
|
||||
// Flipping this will start a transition.
|
||||
bool on = false;
|
||||
|
||||
// Swipe direction for current transition
|
||||
#define SWIPE_UP true
|
||||
#define SWIPE_DOWN false
|
||||
bool swipe = SWIPE_UP;
|
||||
|
||||
// Indicates which Sensor was seen last (to determine
|
||||
// the direction when swiping off)
|
||||
#define LOWER false
|
||||
#define UPPER true
|
||||
bool lastSensor = LOWER;
|
||||
|
||||
// Time of the last transition action
|
||||
unsigned long lastTime = 0;
|
||||
|
||||
// Time of the last sensor check
|
||||
unsigned long lastScanTime = 0;
|
||||
|
||||
// Last time the lights were switched on or off
|
||||
unsigned long lastSwitchTime = 0;
|
||||
|
||||
// segment id between onIndex and offIndex are on.
|
||||
// controll the swipe by setting/moving these indices around.
|
||||
// onIndex must be less than or equal to offIndex
|
||||
byte onIndex = 0;
|
||||
byte offIndex = 0;
|
||||
|
||||
// The maximum number of configured segments.
|
||||
// Dynamically updated based on user configuration.
|
||||
byte maxSegmentId = 1;
|
||||
byte minSegmentId = 0;
|
||||
|
||||
// These values are used by the API to read the
|
||||
// last sensor state, or trigger a sensor
|
||||
// through the API
|
||||
bool topSensorRead = false;
|
||||
bool topSensorWrite = false;
|
||||
bool bottomSensorRead = false;
|
||||
bool bottomSensorWrite = false;
|
||||
bool topSensorState = false;
|
||||
bool bottomSensorState = false;
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _enabled[];
|
||||
static const char _segmentDelay[];
|
||||
static const char _onTime[];
|
||||
static const char _useTopUltrasoundSensor[];
|
||||
static const char _topPIRorTrigger_pin[];
|
||||
static const char _topEcho_pin[];
|
||||
static const char _useBottomUltrasoundSensor[];
|
||||
static const char _bottomPIRorTrigger_pin[];
|
||||
static const char _bottomEcho_pin[];
|
||||
static const char _topEchoCm[];
|
||||
static const char _bottomEchoCm[];
|
||||
static const char _togglePower[];
|
||||
|
||||
void publishMqtt(bool bottom, const char* state) {
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
//Check if MQTT Connected, otherwise it will crash the 8266
|
||||
if (WLED_MQTT_CONNECTED){
|
||||
char subuf[64];
|
||||
sprintf_P(subuf, PSTR("%s/motion/%d"), mqttDeviceTopic, (int)bottom);
|
||||
mqtt->publish(subuf, 0, false, state);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void updateSegments() {
|
||||
for (int i = minSegmentId; i < maxSegmentId; i++) {
|
||||
Segment &seg = strip.getSegment(i);
|
||||
if (!seg.isActive()) continue; // skip gaps
|
||||
if (i >= onIndex && i < offIndex) {
|
||||
seg.setOption(SEG_OPTION_ON, true);
|
||||
// We may need to copy mode and colors from segment 0 to make sure
|
||||
// changes are propagated even when the config is changed during a wipe
|
||||
// seg.setMode(mainsegment.mode);
|
||||
// seg.setColor(0, mainsegment.colors[0]);
|
||||
} else {
|
||||
seg.setOption(SEG_OPTION_ON, false);
|
||||
}
|
||||
// Always mark segments as "transitional", we are animating the staircase
|
||||
//seg.setOption(SEG_OPTION_TRANSITIONAL, true); // not needed anymore as setOption() does it
|
||||
}
|
||||
strip.trigger(); // force strip refresh
|
||||
stateChanged = true; // inform external devices/UI of change
|
||||
colorUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Detects if an object is within ultrasound range.
|
||||
* signalPin: The pin where the pulse is sent
|
||||
* echoPin: The pin where the echo is received
|
||||
* maxTimeUs: Detection timeout in microseconds. If an echo is
|
||||
* received within this time, an object is detected
|
||||
* and the function will return true.
|
||||
*
|
||||
* The speed of sound is 343 meters per second at 20 degrees Celsius.
|
||||
* Since the sound has to travel back and forth, the detection
|
||||
* distance for the sensor in cm is (0.0343 * maxTimeUs) / 2.
|
||||
*
|
||||
* For practical reasons, here are some useful distances:
|
||||
*
|
||||
* Distance = maxtime
|
||||
* 5 cm = 292 uS
|
||||
* 10 cm = 583 uS
|
||||
* 20 cm = 1166 uS
|
||||
* 30 cm = 1749 uS
|
||||
* 50 cm = 2915 uS
|
||||
* 100 cm = 5831 uS
|
||||
*/
|
||||
bool ultrasoundRead(int8_t signalPin, int8_t echoPin, unsigned int maxTimeUs) {
|
||||
if (signalPin<0 || echoPin<0) return false;
|
||||
digitalWrite(signalPin, LOW);
|
||||
delayMicroseconds(2);
|
||||
digitalWrite(signalPin, HIGH);
|
||||
delayMicroseconds(10);
|
||||
digitalWrite(signalPin, LOW);
|
||||
return pulseIn(echoPin, HIGH, maxTimeUs) > 0;
|
||||
}
|
||||
|
||||
bool checkSensors() {
|
||||
bool sensorChanged = false;
|
||||
|
||||
if ((millis() - lastScanTime) > scanDelay) {
|
||||
lastScanTime = millis();
|
||||
|
||||
bottomSensorRead = bottomSensorWrite ||
|
||||
(!useUSSensorBottom ?
|
||||
(bottomPIRorTriggerPin<0 ? false : digitalRead(bottomPIRorTriggerPin)) :
|
||||
ultrasoundRead(bottomPIRorTriggerPin, bottomEchoPin, bottomMaxDist*59) // cm to us
|
||||
);
|
||||
topSensorRead = topSensorWrite ||
|
||||
(!useUSSensorTop ?
|
||||
(topPIRorTriggerPin<0 ? false : digitalRead(topPIRorTriggerPin)) :
|
||||
ultrasoundRead(topPIRorTriggerPin, topEchoPin, topMaxDist*59) // cm to us
|
||||
);
|
||||
|
||||
if (bottomSensorRead != bottomSensorState) {
|
||||
bottomSensorState = bottomSensorRead; // change previous state
|
||||
sensorChanged = true;
|
||||
publishMqtt(true, bottomSensorState ? "on" : "off");
|
||||
DEBUG_PRINTLN(F("Bottom sensor changed."));
|
||||
}
|
||||
|
||||
if (topSensorRead != topSensorState) {
|
||||
topSensorState = topSensorRead; // change previous state
|
||||
sensorChanged = true;
|
||||
publishMqtt(false, topSensorState ? "on" : "off");
|
||||
DEBUG_PRINTLN(F("Top sensor changed."));
|
||||
}
|
||||
|
||||
// Values read, reset the flags for next API call
|
||||
topSensorWrite = false;
|
||||
bottomSensorWrite = false;
|
||||
|
||||
if (topSensorRead != bottomSensorRead) {
|
||||
lastSwitchTime = millis();
|
||||
|
||||
if (on) {
|
||||
lastSensor = topSensorRead;
|
||||
} else {
|
||||
if (togglePower && onIndex == offIndex && offMode) toggleOnOff(); // toggle power on if off
|
||||
// If the bottom sensor triggered, we need to swipe up, ON
|
||||
swipe = bottomSensorRead;
|
||||
|
||||
DEBUG_PRINT(F("ON -> Swipe "));
|
||||
DEBUG_PRINTLN(swipe ? F("up.") : F("down."));
|
||||
|
||||
if (onIndex == offIndex) {
|
||||
// Position the indices for a correct on-swipe
|
||||
if (swipe == SWIPE_UP) {
|
||||
onIndex = minSegmentId;
|
||||
} else {
|
||||
onIndex = maxSegmentId;
|
||||
}
|
||||
offIndex = onIndex;
|
||||
}
|
||||
on = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return sensorChanged;
|
||||
}
|
||||
|
||||
void autoPowerOff() {
|
||||
if ((millis() - lastSwitchTime) > on_time_ms) {
|
||||
// if sensors are still on, do nothing
|
||||
if (bottomSensorState || topSensorState) return;
|
||||
|
||||
// Swipe OFF in the direction of the last sensor detection
|
||||
swipe = lastSensor;
|
||||
on = false;
|
||||
|
||||
DEBUG_PRINT(F("OFF -> Swipe "));
|
||||
DEBUG_PRINTLN(swipe ? F("up.") : F("down."));
|
||||
}
|
||||
}
|
||||
|
||||
void updateSwipe() {
|
||||
if ((millis() - lastTime) > segment_delay_ms) {
|
||||
lastTime = millis();
|
||||
|
||||
byte oldOn = onIndex;
|
||||
byte oldOff = offIndex;
|
||||
if (on) {
|
||||
// Turn on all segments
|
||||
onIndex = MAX(minSegmentId, onIndex - 1);
|
||||
offIndex = MIN(maxSegmentId, offIndex + 1);
|
||||
} else {
|
||||
if (swipe == SWIPE_UP) {
|
||||
onIndex = MIN(offIndex, onIndex + 1);
|
||||
} else {
|
||||
offIndex = MAX(onIndex, offIndex - 1);
|
||||
}
|
||||
}
|
||||
if (oldOn != onIndex || oldOff != offIndex) {
|
||||
updateSegments(); // reduce the number of updates to necessary ones
|
||||
if (togglePower && onIndex == offIndex && !offMode && !on) toggleOnOff(); // toggle power off for all segments off
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// send sensor values to JSON API
|
||||
void writeSensorsToJson(JsonObject& staircase) {
|
||||
staircase[F("top-sensor")] = topSensorRead;
|
||||
staircase[F("bottom-sensor")] = bottomSensorRead;
|
||||
}
|
||||
|
||||
// allow overrides from JSON API
|
||||
void readSensorsFromJson(JsonObject& staircase) {
|
||||
bottomSensorWrite = bottomSensorState || (staircase[F("bottom-sensor")].as<bool>());
|
||||
topSensorWrite = topSensorState || (staircase[F("top-sensor")].as<bool>());
|
||||
}
|
||||
|
||||
void enable(bool enable) {
|
||||
if (enable) {
|
||||
DEBUG_PRINTLN(F("Animated Staircase enabled."));
|
||||
DEBUG_PRINT(F("Delay between steps: "));
|
||||
DEBUG_PRINT(segment_delay_ms);
|
||||
DEBUG_PRINT(F(" milliseconds.\nStairs switch off after: "));
|
||||
DEBUG_PRINT(on_time_ms / 1000);
|
||||
DEBUG_PRINTLN(F(" seconds."));
|
||||
|
||||
if (!useUSSensorBottom)
|
||||
pinMode(bottomPIRorTriggerPin, INPUT_PULLUP);
|
||||
else {
|
||||
pinMode(bottomPIRorTriggerPin, OUTPUT);
|
||||
pinMode(bottomEchoPin, INPUT);
|
||||
}
|
||||
|
||||
if (!useUSSensorTop)
|
||||
pinMode(topPIRorTriggerPin, INPUT_PULLUP);
|
||||
else {
|
||||
pinMode(topPIRorTriggerPin, OUTPUT);
|
||||
pinMode(topEchoPin, INPUT);
|
||||
}
|
||||
onIndex = minSegmentId = strip.getMainSegmentId(); // it may not be the best idea to start with main segment as it may not be the first one
|
||||
offIndex = maxSegmentId = strip.getLastActiveSegmentId() + 1;
|
||||
|
||||
// shorten the strip transition time to be equal or shorter than segment delay
|
||||
transitionDelay = segment_delay_ms;
|
||||
strip.setTransition(segment_delay_ms);
|
||||
strip.trigger();
|
||||
} else {
|
||||
if (togglePower && !on && offMode) toggleOnOff(); // toggle power on if off
|
||||
// Restore segment options
|
||||
for (int i = 0; i <= strip.getLastActiveSegmentId(); i++) {
|
||||
Segment &seg = strip.getSegment(i);
|
||||
if (!seg.isActive()) continue; // skip vector gaps
|
||||
seg.setOption(SEG_OPTION_ON, true);
|
||||
}
|
||||
strip.trigger(); // force strip update
|
||||
stateChanged = true; // inform external devices/UI of change
|
||||
colorUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
DEBUG_PRINTLN(F("Animated Staircase disabled."));
|
||||
}
|
||||
enabled = enable;
|
||||
}
|
||||
|
||||
public:
|
||||
void setup() {
|
||||
// standardize invalid pin numbers to -1
|
||||
if (topPIRorTriggerPin < 0) topPIRorTriggerPin = -1;
|
||||
if (topEchoPin < 0) topEchoPin = -1;
|
||||
if (bottomPIRorTriggerPin < 0) bottomPIRorTriggerPin = -1;
|
||||
if (bottomEchoPin < 0) bottomEchoPin = -1;
|
||||
// allocate pins
|
||||
PinManagerPinType pins[4] = {
|
||||
{ topPIRorTriggerPin, useUSSensorTop },
|
||||
{ topEchoPin, false },
|
||||
{ bottomPIRorTriggerPin, useUSSensorBottom },
|
||||
{ bottomEchoPin, false },
|
||||
};
|
||||
// NOTE: this *WILL* return TRUE if all the pins are set to -1.
|
||||
// this is *BY DESIGN*.
|
||||
if (!PinManager::allocateMultiplePins(pins, 4, PinOwner::UM_AnimatedStaircase)) {
|
||||
topPIRorTriggerPin = -1;
|
||||
topEchoPin = -1;
|
||||
bottomPIRorTriggerPin = -1;
|
||||
bottomEchoPin = -1;
|
||||
enabled = false;
|
||||
}
|
||||
enable(enabled);
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (!enabled || strip.isUpdating()) return;
|
||||
minSegmentId = strip.getMainSegmentId(); // it may not be the best idea to start with main segment as it may not be the first one
|
||||
maxSegmentId = strip.getLastActiveSegmentId() + 1;
|
||||
checkSensors();
|
||||
if (on) autoPowerOff();
|
||||
updateSwipe();
|
||||
}
|
||||
|
||||
uint16_t getId() { return USERMOD_ID_ANIMATED_STAIRCASE; }
|
||||
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
/**
|
||||
* handling of MQTT message
|
||||
* topic only contains stripped topic (part after /wled/MAC)
|
||||
* topic should look like: /swipe with amessage of [up|down]
|
||||
*/
|
||||
bool onMqttMessage(char* topic, char* payload) {
|
||||
if (strlen(topic) == 6 && strncmp_P(topic, PSTR("/swipe"), 6) == 0) {
|
||||
String action = payload;
|
||||
if (action == "up") {
|
||||
bottomSensorWrite = true;
|
||||
return true;
|
||||
} else if (action == "down") {
|
||||
topSensorWrite = true;
|
||||
return true;
|
||||
} else if (action == "on") {
|
||||
enable(true);
|
||||
return true;
|
||||
} else if (action == "off") {
|
||||
enable(false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* subscribe to MQTT topic for controlling usermod
|
||||
*/
|
||||
void onMqttConnect(bool sessionPresent) {
|
||||
//(re)subscribe to required topics
|
||||
char subuf[64];
|
||||
if (mqttDeviceTopic[0] != 0) {
|
||||
strcpy(subuf, mqttDeviceTopic);
|
||||
strcat_P(subuf, PSTR("/swipe"));
|
||||
mqtt->subscribe(subuf, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void addToJsonState(JsonObject& root) {
|
||||
JsonObject staircase = root[FPSTR(_name)];
|
||||
if (staircase.isNull()) {
|
||||
staircase = root.createNestedObject(FPSTR(_name));
|
||||
}
|
||||
writeSensorsToJson(staircase);
|
||||
DEBUG_PRINTLN(F("Staircase sensor state exposed in API."));
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads configuration settings from the json API.
|
||||
* See void addToJsonState(JsonObject& root)
|
||||
*/
|
||||
void readFromJsonState(JsonObject& root) {
|
||||
if (!initDone) return; // prevent crash on boot applyPreset()
|
||||
bool en = enabled;
|
||||
JsonObject staircase = root[FPSTR(_name)];
|
||||
if (!staircase.isNull()) {
|
||||
if (staircase[FPSTR(_enabled)].is<bool>()) {
|
||||
en = staircase[FPSTR(_enabled)].as<bool>();
|
||||
} else {
|
||||
String str = staircase[FPSTR(_enabled)]; // checkbox -> off or on
|
||||
en = (bool)(str!="off"); // off is guaranteed to be present
|
||||
}
|
||||
if (en != enabled) enable(en);
|
||||
readSensorsFromJson(staircase);
|
||||
DEBUG_PRINTLN(F("Staircase sensor state read from API."));
|
||||
}
|
||||
}
|
||||
|
||||
void appendConfigData() {
|
||||
//oappend(F("dd=addDropdown('staircase','selectfield');"));
|
||||
//oappend(F("addOption(dd,'1st value',0);"));
|
||||
//oappend(F("addOption(dd,'2nd value',1);"));
|
||||
//oappend(F("addInfo('staircase:selectfield',1,'additional info');")); // 0 is field type, 1 is actual field
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Writes the configuration to internal flash memory.
|
||||
*/
|
||||
void addToConfig(JsonObject& root) {
|
||||
JsonObject staircase = root[FPSTR(_name)];
|
||||
if (staircase.isNull()) {
|
||||
staircase = root.createNestedObject(FPSTR(_name));
|
||||
}
|
||||
staircase[FPSTR(_enabled)] = enabled;
|
||||
staircase[FPSTR(_segmentDelay)] = segment_delay_ms;
|
||||
staircase[FPSTR(_onTime)] = on_time_ms / 1000;
|
||||
staircase[FPSTR(_useTopUltrasoundSensor)] = useUSSensorTop;
|
||||
staircase[FPSTR(_topPIRorTrigger_pin)] = topPIRorTriggerPin;
|
||||
staircase[FPSTR(_topEcho_pin)] = useUSSensorTop ? topEchoPin : -1;
|
||||
staircase[FPSTR(_useBottomUltrasoundSensor)] = useUSSensorBottom;
|
||||
staircase[FPSTR(_bottomPIRorTrigger_pin)] = bottomPIRorTriggerPin;
|
||||
staircase[FPSTR(_bottomEcho_pin)] = useUSSensorBottom ? bottomEchoPin : -1;
|
||||
staircase[FPSTR(_topEchoCm)] = topMaxDist;
|
||||
staircase[FPSTR(_bottomEchoCm)] = bottomMaxDist;
|
||||
staircase[FPSTR(_togglePower)] = togglePower;
|
||||
DEBUG_PRINTLN(F("Staircase config saved."));
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads the configuration to internal flash memory before setup() is called.
|
||||
*
|
||||
* The function should return true if configuration was successfully loaded or false if there was no configuration.
|
||||
*/
|
||||
bool readFromConfig(JsonObject& root) {
|
||||
bool oldUseUSSensorTop = useUSSensorTop;
|
||||
bool oldUseUSSensorBottom = useUSSensorBottom;
|
||||
int8_t oldTopAPin = topPIRorTriggerPin;
|
||||
int8_t oldTopBPin = topEchoPin;
|
||||
int8_t oldBottomAPin = bottomPIRorTriggerPin;
|
||||
int8_t oldBottomBPin = bottomEchoPin;
|
||||
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
enabled = top[FPSTR(_enabled)] | enabled;
|
||||
|
||||
segment_delay_ms = top[FPSTR(_segmentDelay)] | segment_delay_ms;
|
||||
segment_delay_ms = (unsigned long) min((unsigned long)10000,max((unsigned long)10,(unsigned long)segment_delay_ms)); // max delay 10s
|
||||
|
||||
on_time_ms = top[FPSTR(_onTime)] | on_time_ms/1000;
|
||||
on_time_ms = min(900,max(10,(int)on_time_ms)) * 1000; // min 10s, max 15min
|
||||
|
||||
useUSSensorTop = top[FPSTR(_useTopUltrasoundSensor)] | useUSSensorTop;
|
||||
topPIRorTriggerPin = top[FPSTR(_topPIRorTrigger_pin)] | topPIRorTriggerPin;
|
||||
topEchoPin = top[FPSTR(_topEcho_pin)] | topEchoPin;
|
||||
|
||||
useUSSensorBottom = top[FPSTR(_useBottomUltrasoundSensor)] | useUSSensorBottom;
|
||||
bottomPIRorTriggerPin = top[FPSTR(_bottomPIRorTrigger_pin)] | bottomPIRorTriggerPin;
|
||||
bottomEchoPin = top[FPSTR(_bottomEcho_pin)] | bottomEchoPin;
|
||||
|
||||
topMaxDist = top[FPSTR(_topEchoCm)] | topMaxDist;
|
||||
topMaxDist = min(150,max(30,(int)topMaxDist)); // max distance ~1.5m (a lag of 9ms may be expected)
|
||||
bottomMaxDist = top[FPSTR(_bottomEchoCm)] | bottomMaxDist;
|
||||
bottomMaxDist = min(150,max(30,(int)bottomMaxDist)); // max distance ~1.5m (a lag of 9ms may be expected)
|
||||
|
||||
togglePower = top[FPSTR(_togglePower)] | togglePower; // staircase toggles power on/off
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (!initDone) {
|
||||
// first run: reading from cfg.json
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
// changing parameters from settings page
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
bool changed = false;
|
||||
if ((oldUseUSSensorTop != useUSSensorTop) ||
|
||||
(oldUseUSSensorBottom != useUSSensorBottom) ||
|
||||
(oldTopAPin != topPIRorTriggerPin) ||
|
||||
(oldTopBPin != topEchoPin) ||
|
||||
(oldBottomAPin != bottomPIRorTriggerPin) ||
|
||||
(oldBottomBPin != bottomEchoPin)) {
|
||||
changed = true;
|
||||
PinManager::deallocatePin(oldTopAPin, PinOwner::UM_AnimatedStaircase);
|
||||
PinManager::deallocatePin(oldTopBPin, PinOwner::UM_AnimatedStaircase);
|
||||
PinManager::deallocatePin(oldBottomAPin, PinOwner::UM_AnimatedStaircase);
|
||||
PinManager::deallocatePin(oldBottomBPin, PinOwner::UM_AnimatedStaircase);
|
||||
}
|
||||
if (changed) setup();
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !top[FPSTR(_togglePower)].isNull();
|
||||
}
|
||||
|
||||
/*
|
||||
* Shows the delay between steps and power-off time in the "info"
|
||||
* tab of the web-UI.
|
||||
*/
|
||||
void addToJsonInfo(JsonObject& root) {
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull()) {
|
||||
user = root.createNestedObject("u");
|
||||
}
|
||||
|
||||
JsonArray infoArr = user.createNestedArray(FPSTR(_name)); // name
|
||||
|
||||
String uiDomString = F("<button class=\"btn btn-xs\" onclick=\"requestJson({");
|
||||
uiDomString += FPSTR(_name);
|
||||
uiDomString += F(":{");
|
||||
uiDomString += FPSTR(_enabled);
|
||||
uiDomString += enabled ? F(":false}});\">") : F(":true}});\">");
|
||||
uiDomString += F("<i class=\"icons ");
|
||||
uiDomString += enabled ? "on" : "off";
|
||||
uiDomString += F("\"></i></button>");
|
||||
infoArr.add(uiDomString);
|
||||
}
|
||||
};
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
const char Animated_Staircase::_name[] PROGMEM = "staircase";
|
||||
const char Animated_Staircase::_enabled[] PROGMEM = "enabled";
|
||||
const char Animated_Staircase::_segmentDelay[] PROGMEM = "segment-delay-ms";
|
||||
const char Animated_Staircase::_onTime[] PROGMEM = "on-time-s";
|
||||
const char Animated_Staircase::_useTopUltrasoundSensor[] PROGMEM = "useTopUltrasoundSensor";
|
||||
const char Animated_Staircase::_topPIRorTrigger_pin[] PROGMEM = "topPIRorTrigger_pin";
|
||||
const char Animated_Staircase::_topEcho_pin[] PROGMEM = "topEcho_pin";
|
||||
const char Animated_Staircase::_useBottomUltrasoundSensor[] PROGMEM = "useBottomUltrasoundSensor";
|
||||
const char Animated_Staircase::_bottomPIRorTrigger_pin[] PROGMEM = "bottomPIRorTrigger_pin";
|
||||
const char Animated_Staircase::_bottomEcho_pin[] PROGMEM = "bottomEcho_pin";
|
||||
const char Animated_Staircase::_topEchoCm[] PROGMEM = "top-dist-cm";
|
||||
const char Animated_Staircase::_bottomEchoCm[] PROGMEM = "bottom-dist-cm";
|
||||
const char Animated_Staircase::_togglePower[] PROGMEM = "toggle-on-off";
|
||||
|
||||
|
||||
static Animated_Staircase animated_staircase;
|
||||
REGISTER_USERMOD(animated_staircase);
|
129
usermods/Animated_Staircase/README.md
Normal file
@ -0,0 +1,129 @@
|
||||
# Usermod Animated Staircase
|
||||
This usermod makes your staircase look cool by illuminating it with an animation. It uses
|
||||
PIR or ultrasonic sensors at the top and bottom of your stairs to:
|
||||
|
||||
- Light up the steps in the direction you're walking.
|
||||
- Switch off the steps after you, in the direction of the last detected movement.
|
||||
- Always switch on when one of the sensors detects movement, even if an effect
|
||||
is still running. It can gracefully handle multiple people on the stairs.
|
||||
|
||||
The Animated Staircase can be controlled by the WLED API. Change settings such as
|
||||
speed, on/off time and distance by sending an HTTP request, see below.
|
||||
|
||||
## WLED integration
|
||||
To include this usermod in your WLED setup, you have to be able to [compile WLED from source](https://kno.wled.ge/advanced/compiling-wled/).
|
||||
|
||||
Before compiling, you have to make the following modifications:
|
||||
|
||||
Edit your environment in `platformio_override.ini`
|
||||
1. Open `platformio_override.ini`
|
||||
2. add `Animated_Staircase` to the `custom_usermods` line for your environment
|
||||
|
||||
You can configure usermod using the Usermods settings page.
|
||||
Please enter GPIO pins for PIR or ultrasonic sensors (trigger and echo).
|
||||
If you use PIR sensor enter -1 for echo pin.
|
||||
Maximum distance for ultrasonic sensor can be configured as the time needed for an echo (see below).
|
||||
|
||||
## Hardware installation
|
||||
1. Attach the LED strip to each step of the stairs.
|
||||
2. Connect the ESP8266 pin D4 or ESP32 pin D2 to the first LED data pin at the bottom step.
|
||||
3. Connect the data-out pin at the end of each strip per step to the data-in pin on the
|
||||
next step, creating one large virtual LED strip.
|
||||
4. Mount sensors of choice at the bottom and top of the stairs and connect them to the ESP.
|
||||
5. To make sure all LEDs get enough power and have your staircase lighted evenly, power each
|
||||
step from one side, using at least AWG14 or 2.5mm^2 cable. Don't connect them serial as you
|
||||
do for the datacable!
|
||||
|
||||
You _may_ need to use 10k pull-down resistors on the selected PIR pins, depending on the sensor.
|
||||
|
||||
## WLED configuration
|
||||
1. In the WLED UI, configure a segment for each step. The lowest step of the stairs is the
|
||||
lowest segment id.
|
||||
2. Save your segments into a preset.
|
||||
3. Ideally, add the preset in the config > LED setup menu to the "apply
|
||||
preset **n** at boot" setting.
|
||||
|
||||
## Changing behavior through API
|
||||
The Staircase settings can be changed through the WLED JSON api.
|
||||
|
||||
**NOTE:** We are using [curl](https://curl.se/) to send HTTP POSTs to the WLED API.
|
||||
If you're using Windows and want to use the curl commands, replace the `\` with a `^`
|
||||
or remove them and put everything on one line.
|
||||
|
||||
|
||||
| Setting | Description | Default |
|
||||
|------------------|---------------------------------------------------------------|---------|
|
||||
| enabled | Enable or disable the usermod | true |
|
||||
| bottom-sensor | Manually trigger a down to up animation via API | false |
|
||||
| top-sensor | Manually trigger an up to down animation via API | false |
|
||||
|
||||
|
||||
To read the current settings, open a browser to `http://xxx.xxx.xxx.xxx/json/state` (use your WLED
|
||||
device IP address). The device will respond with a json object containing all WLED settings.
|
||||
The staircase settings and sensor states are inside the WLED "state" element:
|
||||
|
||||
```json
|
||||
{
|
||||
"state": {
|
||||
"staircase": {
|
||||
"enabled": true,
|
||||
"bottom-sensor": false,
|
||||
"top-sensor": false
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Enable/disable the usermod
|
||||
By disabling the usermod you will be able to keep the LED's on, independent from the sensor
|
||||
activity. This enables you to play with the lights without the usermod switching them on or off.
|
||||
|
||||
To disable the usermod:
|
||||
|
||||
```bash
|
||||
curl -X POST -H "Content-Type: application/json" \
|
||||
-d {"staircase":{"enabled":false}} \
|
||||
xxx.xxx.xxx.xxx/json/state
|
||||
```
|
||||
|
||||
To enable the usermod again, use `"enabled":true`.
|
||||
|
||||
Alternatively you can use _Usermod_ Settings page where you can change other parameters as well.
|
||||
|
||||
### Changing animation parameters and detection range of the ultrasonic HC-SR04 sensor
|
||||
Using _Usermod_ Settings page you can define different usermod parameters, including sensor pins, delay between segment activation etc.
|
||||
|
||||
When an ultrasonic sensor is enabled you can enter maximum detection distance in centimeters separately for top and bottom sensors.
|
||||
|
||||
**Please note:** using an HC-SR04 sensor, particularly when detecting echos at longer
|
||||
distances creates delays in the WLED software, _might_ introduce timing hiccups in your animation or
|
||||
a less responsive web interface. It is therefore advised to keep the detection distance as short as possible.
|
||||
|
||||
### Animation triggering through the API
|
||||
In addition to activation by one of the stair sensors, you can also trigger the animation manually
|
||||
via the API. To simulate triggering the bottom sensor, use:
|
||||
|
||||
```bash
|
||||
curl -X POST -H "Content-Type: application/json" \
|
||||
-d '{"staircase":{"bottom-sensor":true}}' \
|
||||
xxx.xxx.xxx.xxx/json/state
|
||||
```
|
||||
|
||||
Likewise, to trigger the top sensor:
|
||||
|
||||
```bash
|
||||
curl -X POST -H "Content-Type: application/json" \
|
||||
-d '{"staircase":{"top-sensor":true}}' \
|
||||
xxx.xxx.xxx.xxx/json/state
|
||||
```
|
||||
**MQTT**
|
||||
You can publish a message with either `up` or `down` on topic `/swipe` to trigger animation.
|
||||
You can also use `on` or `off` for enabling or disabling the usermod.
|
||||
|
||||
Have fun with this usermod.<br/>
|
||||
www.rolfje.com
|
||||
|
||||
Modifications @blazoncek
|
||||
|
||||
## Change log
|
||||
2021-04
|
||||
* Adaptation for runtime configuration.
|
3
usermods/Animated_Staircase/library.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"name:": "Animated_Staircase"
|
||||
}
|
5
usermods/Artemis_reciever/readme.md
Normal file
@ -0,0 +1,5 @@
|
||||
Usermod to allow WLED to receive via UDP port from RGB.NET (and therefore add as a device to be controlled within artemis on PC)
|
||||
|
||||
This is only a very simple code to support a single led strip, it does not support the full function of the RGB.NET sketch for esp8266 only what is needed to be used with Artemis. It will show as a ws281x device in artemis when you provide the correct hostname or ip. Artemis queries the number of LEDs via the web interface (/config) but communication to set the LEDs is all done via the UDP interface.
|
||||
|
||||
To install, copy the usermod.cpp file to wled00 folder and recompile
|
93
usermods/Artemis_reciever/usermod.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* RGB.NET (artemis) receiver
|
||||
*
|
||||
* This works via the UDP, http is not supported apart from reporting LED count
|
||||
*
|
||||
*
|
||||
*/
|
||||
#include "wled.h"
|
||||
#include <WiFiUdp.h>
|
||||
|
||||
WiFiUDP UDP;
|
||||
const unsigned int RGBNET_localUdpPort = 1872; // local port to listen on
|
||||
unsigned char RGBNET_packet[770];
|
||||
long lastTime = 0;
|
||||
int delayMs = 10;
|
||||
bool isRGBNETUDPEnabled;
|
||||
|
||||
void RGBNET_readValues() {
|
||||
|
||||
int RGBNET_packetSize = UDP.parsePacket();
|
||||
if (RGBNET_packetSize) {
|
||||
// receive incoming UDP packets
|
||||
int sequenceNumber = UDP.read();
|
||||
int channel = UDP.read();
|
||||
|
||||
//channel data is not used we only supports one channel
|
||||
int len = UDP.read(RGBNET_packet, strip.getLengthTotal()*3);
|
||||
if(len==0){
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < len; i=i+3) {
|
||||
strip.setPixelColor(i/3, RGBNET_packet[i], RGBNET_packet[i+1], RGBNET_packet[i+2], 0);
|
||||
}
|
||||
//strip.show();
|
||||
}
|
||||
}
|
||||
|
||||
//update LED strip
|
||||
void RGBNET_show() {
|
||||
strip.show();
|
||||
lastTime = millis();
|
||||
}
|
||||
|
||||
//This function provides a json with info on the number of LEDs connected
|
||||
// it is needed by artemis to know how many LEDs to display on the surface
|
||||
void handleConfig(AsyncWebServerRequest *request)
|
||||
{
|
||||
String config = (String)"{\
|
||||
\"channels\": [\
|
||||
{\
|
||||
\"channel\": 1,\
|
||||
\"leds\": " + strip.getLengthTotal() + "\
|
||||
},\
|
||||
{\
|
||||
\"channel\": 2,\
|
||||
\"leds\": " + "0" + "\
|
||||
},\
|
||||
{\
|
||||
\"channel\": 3,\
|
||||
\"leds\": " + "0" + "\
|
||||
},\
|
||||
{\
|
||||
\"channel\": 4,\
|
||||
\"leds\": " + "0" + "\
|
||||
}\
|
||||
]\
|
||||
}";
|
||||
request->send(200, "application/json", config);
|
||||
}
|
||||
|
||||
|
||||
void userSetup()
|
||||
{
|
||||
server.on("/config", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
handleConfig(request);
|
||||
});
|
||||
}
|
||||
|
||||
void userConnected()
|
||||
{
|
||||
// new wifi, who dis?
|
||||
UDP.begin(RGBNET_localUdpPort);
|
||||
isRGBNETUDPEnabled = true;
|
||||
}
|
||||
|
||||
void userLoop()
|
||||
{
|
||||
RGBNET_readValues();
|
||||
if (millis()-lastTime > delayMs) {
|
||||
RGBNET_show();
|
||||
}
|
||||
}
|
254
usermods/BH1750_v2/BH1750_v2.cpp
Normal file
@ -0,0 +1,254 @@
|
||||
// force the compiler to show a warning to confirm that this file is included
|
||||
#warning **** Included USERMOD_BH1750 ****
|
||||
|
||||
#include "wled.h"
|
||||
#include <BH1750.h>
|
||||
|
||||
#ifdef WLED_DISABLE_MQTT
|
||||
#error "This user mod requires MQTT to be enabled."
|
||||
#endif
|
||||
|
||||
// the max frequency to check photoresistor, 10 seconds
|
||||
#ifndef USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL
|
||||
#define USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL 10000
|
||||
#endif
|
||||
|
||||
// the min frequency to check photoresistor, 500 ms
|
||||
#ifndef USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL
|
||||
#define USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL 500
|
||||
#endif
|
||||
|
||||
// how many seconds after boot to take first measurement, 10 seconds
|
||||
#ifndef USERMOD_BH1750_FIRST_MEASUREMENT_AT
|
||||
#define USERMOD_BH1750_FIRST_MEASUREMENT_AT 10000
|
||||
#endif
|
||||
|
||||
// only report if difference grater than offset value
|
||||
#ifndef USERMOD_BH1750_OFFSET_VALUE
|
||||
#define USERMOD_BH1750_OFFSET_VALUE 1
|
||||
#endif
|
||||
|
||||
class Usermod_BH1750 : public Usermod
|
||||
{
|
||||
private:
|
||||
int8_t offset = USERMOD_BH1750_OFFSET_VALUE;
|
||||
|
||||
unsigned long maxReadingInterval = USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL;
|
||||
unsigned long minReadingInterval = USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL;
|
||||
unsigned long lastMeasurement = UINT32_MAX - (USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL - USERMOD_BH1750_FIRST_MEASUREMENT_AT);
|
||||
unsigned long lastSend = UINT32_MAX - (USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL - USERMOD_BH1750_FIRST_MEASUREMENT_AT);
|
||||
// flag to indicate we have finished the first readLightLevel call
|
||||
// allows this library to report to the user how long until the first
|
||||
// measurement
|
||||
bool getLuminanceComplete = false;
|
||||
|
||||
// flag set at startup
|
||||
bool enabled = true;
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _enabled[];
|
||||
static const char _maxReadInterval[];
|
||||
static const char _minReadInterval[];
|
||||
static const char _offset[];
|
||||
static const char _HomeAssistantDiscovery[];
|
||||
|
||||
bool initDone = false;
|
||||
bool sensorFound = false;
|
||||
|
||||
// Home Assistant and MQTT
|
||||
String mqttLuminanceTopic;
|
||||
bool mqttInitialized = false;
|
||||
bool HomeAssistantDiscovery = true; // Publish Home Assistant Discovery messages
|
||||
|
||||
BH1750 lightMeter;
|
||||
float lastLux = -1000;
|
||||
|
||||
bool checkBoundSensor(float newValue, float prevValue, float maxDiff)
|
||||
{
|
||||
return isnan(prevValue) || newValue <= prevValue - maxDiff || newValue >= prevValue + maxDiff || (newValue == 0.0 && prevValue > 0.0);
|
||||
}
|
||||
|
||||
// set up Home Assistant discovery entries
|
||||
void _mqttInitialize()
|
||||
{
|
||||
mqttLuminanceTopic = String(mqttDeviceTopic) + F("/brightness");
|
||||
|
||||
if (HomeAssistantDiscovery) _createMqttSensor(F("Brightness"), mqttLuminanceTopic, F("Illuminance"), F(" lx"));
|
||||
}
|
||||
|
||||
// Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop.
|
||||
void _createMqttSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement)
|
||||
{
|
||||
String t = String(F("homeassistant/sensor/")) + mqttClientID + F("/") + name + F("/config");
|
||||
|
||||
StaticJsonDocument<600> doc;
|
||||
|
||||
doc[F("name")] = String(serverDescription) + " " + name;
|
||||
doc[F("state_topic")] = topic;
|
||||
doc[F("unique_id")] = String(mqttClientID) + name;
|
||||
if (unitOfMeasurement != "")
|
||||
doc[F("unit_of_measurement")] = unitOfMeasurement;
|
||||
if (deviceClass != "")
|
||||
doc[F("device_class")] = deviceClass;
|
||||
doc[F("expire_after")] = 1800;
|
||||
|
||||
JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device
|
||||
device[F("name")] = serverDescription;
|
||||
device[F("identifiers")] = "wled-sensor-" + String(mqttClientID);
|
||||
device[F("manufacturer")] = F(WLED_BRAND);
|
||||
device[F("model")] = F(WLED_PRODUCT_NAME);
|
||||
device[F("sw_version")] = versionString;
|
||||
|
||||
String temp;
|
||||
serializeJson(doc, temp);
|
||||
DEBUG_PRINTLN(t);
|
||||
DEBUG_PRINTLN(temp);
|
||||
|
||||
mqtt->publish(t.c_str(), 0, true, temp.c_str());
|
||||
}
|
||||
|
||||
public:
|
||||
void setup()
|
||||
{
|
||||
if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; }
|
||||
sensorFound = lightMeter.begin();
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
if ((!enabled) || strip.isUpdating())
|
||||
return;
|
||||
|
||||
unsigned long now = millis();
|
||||
|
||||
// check to see if we are due for taking a measurement
|
||||
// lastMeasurement will not be updated until the conversion
|
||||
// is complete the the reading is finished
|
||||
if (now - lastMeasurement < minReadingInterval)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool shouldUpdate = now - lastSend > maxReadingInterval;
|
||||
|
||||
float lux = lightMeter.readLightLevel();
|
||||
lastMeasurement = millis();
|
||||
getLuminanceComplete = true;
|
||||
|
||||
if (shouldUpdate || checkBoundSensor(lux, lastLux, offset))
|
||||
{
|
||||
lastLux = lux;
|
||||
lastSend = millis();
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
if (WLED_MQTT_CONNECTED)
|
||||
{
|
||||
if (!mqttInitialized)
|
||||
{
|
||||
_mqttInitialize();
|
||||
mqttInitialized = true;
|
||||
}
|
||||
mqtt->publish(mqttLuminanceTopic.c_str(), 0, true, String(lux).c_str());
|
||||
DEBUG_PRINTLN(F("Brightness: ") + String(lux) + F("lx"));
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_PRINTLN(F("Missing MQTT connection. Not publishing data"));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
inline float getIlluminance() {
|
||||
return (float)lastLux;
|
||||
}
|
||||
|
||||
void addToJsonInfo(JsonObject &root)
|
||||
{
|
||||
JsonObject user = root[F("u")];
|
||||
if (user.isNull())
|
||||
user = root.createNestedObject(F("u"));
|
||||
|
||||
JsonArray lux_json = user.createNestedArray(F("Luminance"));
|
||||
if (!enabled) {
|
||||
lux_json.add(F("disabled"));
|
||||
} else if (!sensorFound) {
|
||||
// if no sensor
|
||||
lux_json.add(F("BH1750 "));
|
||||
lux_json.add(F("Not Found"));
|
||||
} else if (!getLuminanceComplete) {
|
||||
// if we haven't read the sensor yet, let the user know
|
||||
// that we are still waiting for the first measurement
|
||||
lux_json.add((USERMOD_BH1750_FIRST_MEASUREMENT_AT - millis()) / 1000);
|
||||
lux_json.add(F(" sec until read"));
|
||||
return;
|
||||
} else {
|
||||
lux_json.add(lastLux);
|
||||
lux_json.add(F(" lx"));
|
||||
}
|
||||
}
|
||||
|
||||
// (called from set.cpp) stores persistent properties to cfg.json
|
||||
void addToConfig(JsonObject &root)
|
||||
{
|
||||
// we add JSON object.
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top[FPSTR(_maxReadInterval)] = maxReadingInterval;
|
||||
top[FPSTR(_minReadInterval)] = minReadingInterval;
|
||||
top[FPSTR(_HomeAssistantDiscovery)] = HomeAssistantDiscovery;
|
||||
top[FPSTR(_offset)] = offset;
|
||||
|
||||
DEBUG_PRINTLN(F("BH1750 config saved."));
|
||||
}
|
||||
|
||||
// called before setup() to populate properties from values stored in cfg.json
|
||||
bool readFromConfig(JsonObject &root)
|
||||
{
|
||||
// we look for JSON object.
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull())
|
||||
{
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
DEBUG_PRINT(F("BH1750"));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
bool configComplete = !top.isNull();
|
||||
|
||||
configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled, false);
|
||||
configComplete &= getJsonValue(top[FPSTR(_maxReadInterval)], maxReadingInterval, 10000); //ms
|
||||
configComplete &= getJsonValue(top[FPSTR(_minReadInterval)], minReadingInterval, 500); //ms
|
||||
configComplete &= getJsonValue(top[FPSTR(_HomeAssistantDiscovery)], HomeAssistantDiscovery, false);
|
||||
configComplete &= getJsonValue(top[FPSTR(_offset)], offset, 1);
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (!initDone) {
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
}
|
||||
|
||||
return configComplete;
|
||||
|
||||
}
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_BH1750;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
const char Usermod_BH1750::_name[] PROGMEM = "BH1750";
|
||||
const char Usermod_BH1750::_enabled[] PROGMEM = "enabled";
|
||||
const char Usermod_BH1750::_maxReadInterval[] PROGMEM = "max-read-interval-ms";
|
||||
const char Usermod_BH1750::_minReadInterval[] PROGMEM = "min-read-interval-ms";
|
||||
const char Usermod_BH1750::_HomeAssistantDiscovery[] PROGMEM = "HomeAssistantDiscoveryLux";
|
||||
const char Usermod_BH1750::_offset[] PROGMEM = "offset-lx";
|
||||
|
||||
|
||||
static Usermod_BH1750 bh1750_v2;
|
||||
REGISTER_USERMOD(bh1750_v2);
|
6
usermods/BH1750_v2/library.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name:": "BH1750_v2",
|
||||
"dependencies": {
|
||||
"claws/BH1750":"^1.2.0"
|
||||
}
|
||||
}
|
38
usermods/BH1750_v2/readme.md
Normal file
@ -0,0 +1,38 @@
|
||||
# BH1750 usermod
|
||||
|
||||
This usermod will read from an ambient light sensor like the BH1750.
|
||||
The luminance is displayed in both the Info section of the web UI, as well as published to the `/luminance` MQTT topic if enabled.
|
||||
|
||||
## Dependencies
|
||||
- Libraries
|
||||
- `claws/BH1750 @^1.2.0`
|
||||
- Data is published over MQTT - make sure you've enabled the MQTT sync interface.
|
||||
|
||||
## Compilation
|
||||
|
||||
To enable, compile with `BH1750` in `custom_usermods` (e.g. in `platformio_override.ini`)
|
||||
|
||||
### Configuration Options
|
||||
The following settings can be set at compile-time but are configurable on the usermod menu (except First Measurement time):
|
||||
* `USERMOD_BH1750_MAX_MEASUREMENT_INTERVAL` - the max number of milliseconds between measurements, defaults to 10000ms
|
||||
* `USERMOD_BH1750_MIN_MEASUREMENT_INTERVAL` - the min number of milliseconds between measurements, defaults to 500ms
|
||||
* `USERMOD_BH1750_OFFSET_VALUE` - the offset value to report on, defaults to 1
|
||||
* `USERMOD_BH1750_FIRST_MEASUREMENT_AT` - the number of milliseconds after boot to take first measurement, defaults to 10000 ms
|
||||
|
||||
In addition, the Usermod screen allows you to:
|
||||
- enable/disable the usermod
|
||||
- Enable Home Assistant Discovery of usermod
|
||||
- Configure the SCL/SDA pins
|
||||
|
||||
## API
|
||||
The following method is available to interact with the usermod from other code modules:
|
||||
- `getIlluminance` read the brightness from the sensor
|
||||
|
||||
## Change Log
|
||||
Jul 2022
|
||||
- Added Home Assistant Discovery
|
||||
- Implemented PinManager to register pins
|
||||
- Made pins configurable in usermod menu
|
||||
- Added API call to read luminance from other modules
|
||||
- Enhanced info-screen outputs
|
||||
- Updated `readme.md`
|
483
usermods/BME280_v2/BME280_v2.cpp
Normal file
@ -0,0 +1,483 @@
|
||||
// force the compiler to show a warning to confirm that this file is included
|
||||
#warning **** Included USERMOD_BME280 version 2.0 ****
|
||||
|
||||
#include "wled.h"
|
||||
#include <Arduino.h>
|
||||
#include <BME280I2C.h> // BME280 sensor
|
||||
#include <EnvironmentCalculations.h> // BME280 extended measurements
|
||||
|
||||
#ifdef WLED_DISABLE_MQTT
|
||||
#error "This user mod requires MQTT to be enabled."
|
||||
#endif
|
||||
|
||||
class UsermodBME280 : public Usermod
|
||||
{
|
||||
private:
|
||||
|
||||
// NOTE: Do not implement any compile-time variables, anything the user needs to configure
|
||||
// should be configurable from the Usermod menu using the methods below
|
||||
// key settings set via usermod menu
|
||||
uint8_t TemperatureDecimals = 0; // Number of decimal places in published temperaure values
|
||||
uint8_t HumidityDecimals = 0; // Number of decimal places in published humidity values
|
||||
uint8_t PressureDecimals = 0; // Number of decimal places in published pressure values
|
||||
uint16_t TemperatureInterval = 5; // Interval to measure temperature (and humidity, dew point if available) in seconds
|
||||
uint16_t PressureInterval = 300; // Interval to measure pressure in seconds
|
||||
BME280I2C::I2CAddr I2CAddress = BME280I2C::I2CAddr_0x76; // i2c address, defaults to 0x76
|
||||
bool PublishAlways = false; // Publish values even when they have not changed
|
||||
bool UseCelsius = true; // Use Celsius for Reporting
|
||||
bool HomeAssistantDiscovery = false; // Publish Home Assistant Device Information
|
||||
bool enabled = true;
|
||||
|
||||
// set the default pins based on the architecture, these get overridden by Usermod menu settings
|
||||
#ifdef ESP8266
|
||||
//uint8_t RST_PIN = 16; // Un-comment for Heltec WiFi-Kit-8
|
||||
#endif
|
||||
bool initDone = false;
|
||||
|
||||
BME280I2C bme;
|
||||
|
||||
uint8_t sensorType;
|
||||
|
||||
// Measurement timers
|
||||
long timer;
|
||||
long lastTemperatureMeasure = 0;
|
||||
long lastPressureMeasure = 0;
|
||||
|
||||
// Current sensor values
|
||||
float sensorTemperature;
|
||||
float sensorHumidity;
|
||||
float sensorHeatIndex;
|
||||
float sensorDewPoint;
|
||||
float sensorPressure;
|
||||
String tempScale;
|
||||
// Track previous sensor values
|
||||
float lastTemperature;
|
||||
float lastHumidity;
|
||||
float lastHeatIndex;
|
||||
float lastDewPoint;
|
||||
float lastPressure;
|
||||
|
||||
// MQTT topic strings for publishing Home Assistant discovery topics
|
||||
bool mqttInitialized = false;
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _enabled[];
|
||||
|
||||
// Read the BME280/BMP280 Sensor (which one runs depends on whether Celsius or Fahrenheit being set in Usermod Menu)
|
||||
void UpdateBME280Data(int SensorType)
|
||||
{
|
||||
float _temperature, _humidity, _pressure;
|
||||
|
||||
if (UseCelsius) {
|
||||
BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
|
||||
EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Celsius);
|
||||
BME280::PresUnit presUnit(BME280::PresUnit_hPa);
|
||||
|
||||
bme.read(_pressure, _temperature, _humidity, tempUnit, presUnit);
|
||||
|
||||
sensorTemperature = _temperature;
|
||||
sensorHumidity = _humidity;
|
||||
sensorPressure = _pressure;
|
||||
tempScale = F("°C");
|
||||
if (sensorType == 1)
|
||||
{
|
||||
sensorHeatIndex = EnvironmentCalculations::HeatIndex(_temperature, _humidity, envTempUnit);
|
||||
sensorDewPoint = EnvironmentCalculations::DewPoint(_temperature, _humidity, envTempUnit);
|
||||
}
|
||||
} else {
|
||||
BME280::TempUnit tempUnit(BME280::TempUnit_Fahrenheit);
|
||||
EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Fahrenheit);
|
||||
BME280::PresUnit presUnit(BME280::PresUnit_hPa);
|
||||
|
||||
bme.read(_pressure, _temperature, _humidity, tempUnit, presUnit);
|
||||
|
||||
sensorTemperature = _temperature;
|
||||
sensorHumidity = _humidity;
|
||||
sensorPressure = _pressure;
|
||||
tempScale = F("°F");
|
||||
if (sensorType == 1)
|
||||
{
|
||||
sensorHeatIndex = EnvironmentCalculations::HeatIndex(_temperature, _humidity, envTempUnit);
|
||||
sensorDewPoint = EnvironmentCalculations::DewPoint(_temperature, _humidity, envTempUnit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Procedure to define all MQTT discovery Topics
|
||||
void _mqttInitialize()
|
||||
{
|
||||
char mqttTemperatureTopic[128];
|
||||
char mqttHumidityTopic[128];
|
||||
char mqttPressureTopic[128];
|
||||
char mqttHeatIndexTopic[128];
|
||||
char mqttDewPointTopic[128];
|
||||
snprintf_P(mqttTemperatureTopic, 127, PSTR("%s/temperature"), mqttDeviceTopic);
|
||||
snprintf_P(mqttPressureTopic, 127, PSTR("%s/pressure"), mqttDeviceTopic);
|
||||
snprintf_P(mqttHumidityTopic, 127, PSTR("%s/humidity"), mqttDeviceTopic);
|
||||
snprintf_P(mqttHeatIndexTopic, 127, PSTR("%s/heat_index"), mqttDeviceTopic);
|
||||
snprintf_P(mqttDewPointTopic, 127, PSTR("%s/dew_point"), mqttDeviceTopic);
|
||||
|
||||
if (HomeAssistantDiscovery) {
|
||||
_createMqttSensor(F("Temperature"), mqttTemperatureTopic, "temperature", tempScale);
|
||||
_createMqttSensor(F("Pressure"), mqttPressureTopic, "pressure", F("hPa"));
|
||||
_createMqttSensor(F("Humidity"), mqttHumidityTopic, "humidity", F("%"));
|
||||
_createMqttSensor(F("HeatIndex"), mqttHeatIndexTopic, "temperature", tempScale);
|
||||
_createMqttSensor(F("DewPoint"), mqttDewPointTopic, "temperature", tempScale);
|
||||
}
|
||||
}
|
||||
|
||||
// Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop.
|
||||
void _createMqttSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement)
|
||||
{
|
||||
String t = String(F("homeassistant/sensor/")) + mqttClientID + F("/") + name + F("/config");
|
||||
|
||||
StaticJsonDocument<600> doc;
|
||||
|
||||
doc[F("name")] = String(serverDescription) + " " + name;
|
||||
doc[F("state_topic")] = topic;
|
||||
doc[F("unique_id")] = String(mqttClientID) + name;
|
||||
if (unitOfMeasurement != "")
|
||||
doc[F("unit_of_measurement")] = unitOfMeasurement;
|
||||
if (deviceClass != "")
|
||||
doc[F("device_class")] = deviceClass;
|
||||
doc[F("expire_after")] = 1800;
|
||||
|
||||
JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device
|
||||
device[F("name")] = serverDescription;
|
||||
device[F("identifiers")] = "wled-sensor-" + String(mqttClientID);
|
||||
device[F("manufacturer")] = F(WLED_BRAND);
|
||||
device[F("model")] = F(WLED_PRODUCT_NAME);
|
||||
device[F("sw_version")] = versionString;
|
||||
|
||||
String temp;
|
||||
serializeJson(doc, temp);
|
||||
DEBUG_PRINTLN(t);
|
||||
DEBUG_PRINTLN(temp);
|
||||
|
||||
mqtt->publish(t.c_str(), 0, true, temp.c_str());
|
||||
}
|
||||
|
||||
void publishMqtt(const char *topic, const char* state) {
|
||||
//Check if MQTT Connected, otherwise it will crash the 8266
|
||||
if (WLED_MQTT_CONNECTED){
|
||||
char subuf[128];
|
||||
snprintf_P(subuf, 127, PSTR("%s/%s"), mqttDeviceTopic, topic);
|
||||
mqtt->publish(subuf, 0, false, state);
|
||||
}
|
||||
}
|
||||
|
||||
void initializeBmeComms()
|
||||
{
|
||||
BME280I2C::Settings settings{
|
||||
BME280::OSR_X16, // Temperature oversampling x16
|
||||
BME280::OSR_X16, // Humidity oversampling x16
|
||||
BME280::OSR_X16, // Pressure oversampling x16
|
||||
BME280::Mode_Forced,
|
||||
BME280::StandbyTime_1000ms,
|
||||
BME280::Filter_Off,
|
||||
BME280::SpiEnable_False,
|
||||
I2CAddress
|
||||
};
|
||||
|
||||
bme.setSettings(settings);
|
||||
|
||||
if (!bme.begin())
|
||||
{
|
||||
sensorType = 0;
|
||||
DEBUG_PRINTLN(F("Could not find BME280 I2C sensor!"));
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (bme.chipModel())
|
||||
{
|
||||
case BME280::ChipModel_BME280:
|
||||
sensorType = 1;
|
||||
DEBUG_PRINTLN(F("Found BME280 sensor! Success."));
|
||||
break;
|
||||
case BME280::ChipModel_BMP280:
|
||||
sensorType = 2;
|
||||
DEBUG_PRINTLN(F("Found BMP280 sensor! No Humidity available."));
|
||||
break;
|
||||
default:
|
||||
sensorType = 0;
|
||||
DEBUG_PRINTLN(F("Found UNKNOWN sensor! Error!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void setup()
|
||||
{
|
||||
if (i2c_scl<0 || i2c_sda<0) { enabled = false; sensorType = 0; return; }
|
||||
|
||||
initializeBmeComms();
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (!enabled || strip.isUpdating()) return;
|
||||
|
||||
// BME280 sensor MQTT publishing
|
||||
// Check if sensor present and Connected, otherwise it will crash the MCU
|
||||
if (sensorType != 0)
|
||||
{
|
||||
// Timer to fetch new temperature, humidity and pressure data at intervals
|
||||
timer = millis();
|
||||
|
||||
if (timer - lastTemperatureMeasure >= TemperatureInterval * 1000)
|
||||
{
|
||||
lastTemperatureMeasure = timer;
|
||||
|
||||
UpdateBME280Data(sensorType);
|
||||
|
||||
float temperature = roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
|
||||
float humidity, heatIndex, dewPoint;
|
||||
|
||||
// If temperature has changed since last measure, create string populated with device topic
|
||||
// from the UI and values read from sensor, then publish to broker
|
||||
if (temperature != lastTemperature || PublishAlways)
|
||||
{
|
||||
publishMqtt("temperature", String(temperature, (unsigned) TemperatureDecimals).c_str());
|
||||
}
|
||||
|
||||
lastTemperature = temperature; // Update last sensor temperature for next loop
|
||||
|
||||
if (sensorType == 1) // Only if sensor is a BME280
|
||||
{
|
||||
humidity = roundf(sensorHumidity * powf(10, HumidityDecimals)) / powf(10, HumidityDecimals);
|
||||
heatIndex = roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
|
||||
dewPoint = roundf(sensorDewPoint * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
|
||||
|
||||
if (humidity != lastHumidity || PublishAlways)
|
||||
{
|
||||
publishMqtt("humidity", String(humidity, (unsigned) HumidityDecimals).c_str());
|
||||
}
|
||||
|
||||
if (heatIndex != lastHeatIndex || PublishAlways)
|
||||
{
|
||||
publishMqtt("heat_index", String(heatIndex, (unsigned) TemperatureDecimals).c_str());
|
||||
}
|
||||
|
||||
if (dewPoint != lastDewPoint || PublishAlways)
|
||||
{
|
||||
publishMqtt("dew_point", String(dewPoint, (unsigned) TemperatureDecimals).c_str());
|
||||
}
|
||||
|
||||
lastHumidity = humidity;
|
||||
lastHeatIndex = heatIndex;
|
||||
lastDewPoint = dewPoint;
|
||||
}
|
||||
}
|
||||
|
||||
if (timer - lastPressureMeasure >= PressureInterval * 1000)
|
||||
{
|
||||
lastPressureMeasure = timer;
|
||||
|
||||
float pressure = roundf(sensorPressure * powf(10, PressureDecimals)) / powf(10, PressureDecimals);
|
||||
|
||||
if (pressure != lastPressure || PublishAlways)
|
||||
{
|
||||
publishMqtt("pressure", String(pressure, (unsigned) PressureDecimals).c_str());
|
||||
}
|
||||
|
||||
lastPressure = pressure;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onMqttConnect(bool sessionPresent)
|
||||
{
|
||||
if (WLED_MQTT_CONNECTED && !mqttInitialized)
|
||||
{
|
||||
_mqttInitialize();
|
||||
mqttInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* API calls te enable data exchange between WLED modules
|
||||
*/
|
||||
inline float getTemperatureC() {
|
||||
if (UseCelsius) {
|
||||
return (float)roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
|
||||
} else {
|
||||
return (float)roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals) * 1.8f + 32;
|
||||
}
|
||||
}
|
||||
|
||||
inline float getTemperatureF() {
|
||||
if (UseCelsius) {
|
||||
return ((float)roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals) -32) * 0.56f;
|
||||
} else {
|
||||
return (float)roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
|
||||
}
|
||||
}
|
||||
|
||||
inline float getHumidity() {
|
||||
return (float)roundf(sensorHumidity * powf(10, HumidityDecimals));
|
||||
}
|
||||
|
||||
inline float getPressure() {
|
||||
return (float)roundf(sensorPressure * powf(10, PressureDecimals));
|
||||
}
|
||||
|
||||
inline float getDewPointC() {
|
||||
if (UseCelsius) {
|
||||
return (float)roundf(sensorDewPoint * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
|
||||
} else {
|
||||
return (float)roundf(sensorDewPoint * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals) * 1.8f + 32;
|
||||
}
|
||||
}
|
||||
|
||||
inline float getDewPointF() {
|
||||
if (UseCelsius) {
|
||||
return ((float)roundf(sensorDewPoint * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals) -32) * 0.56f;
|
||||
} else {
|
||||
return (float)roundf(sensorDewPoint * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
|
||||
}
|
||||
}
|
||||
|
||||
inline float getHeatIndexC() {
|
||||
if (UseCelsius) {
|
||||
return (float)roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
|
||||
} else {
|
||||
return (float)roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals) * 1.8f + 32;
|
||||
}
|
||||
}
|
||||
|
||||
inline float getHeatIndexF() {
|
||||
if (UseCelsius) {
|
||||
return ((float)roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals) -32) * 0.56f;
|
||||
} else {
|
||||
return (float)roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
|
||||
}
|
||||
}
|
||||
|
||||
// Publish Sensor Information to Info Page
|
||||
void addToJsonInfo(JsonObject &root)
|
||||
{
|
||||
JsonObject user = root[F("u")];
|
||||
if (user.isNull()) user = root.createNestedObject(F("u"));
|
||||
|
||||
if (sensorType==0) //No Sensor
|
||||
{
|
||||
// if we sensor not detected, let the user know
|
||||
JsonArray temperature_json = user.createNestedArray(F("BME/BMP280 Sensor"));
|
||||
temperature_json.add(F("Not Found"));
|
||||
}
|
||||
else if (sensorType==2) //BMP280
|
||||
{
|
||||
JsonArray temperature_json = user.createNestedArray(F("Temperature"));
|
||||
JsonArray pressure_json = user.createNestedArray(F("Pressure"));
|
||||
temperature_json.add(roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals));
|
||||
temperature_json.add(tempScale);
|
||||
pressure_json.add(roundf(sensorPressure * powf(10, PressureDecimals)) / powf(10, PressureDecimals));
|
||||
pressure_json.add(F("hPa"));
|
||||
}
|
||||
else if (sensorType==1) //BME280
|
||||
{
|
||||
JsonArray temperature_json = user.createNestedArray(F("Temperature"));
|
||||
JsonArray humidity_json = user.createNestedArray(F("Humidity"));
|
||||
JsonArray pressure_json = user.createNestedArray(F("Pressure"));
|
||||
JsonArray heatindex_json = user.createNestedArray(F("Heat Index"));
|
||||
JsonArray dewpoint_json = user.createNestedArray(F("Dew Point"));
|
||||
temperature_json.add(roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals));
|
||||
temperature_json.add(tempScale);
|
||||
humidity_json.add(roundf(sensorHumidity * powf(10, HumidityDecimals)) / powf(10, HumidityDecimals));
|
||||
humidity_json.add(F("%"));
|
||||
pressure_json.add(roundf(sensorPressure * powf(10, PressureDecimals)) / powf(10, PressureDecimals));
|
||||
pressure_json.add(F("hPa"));
|
||||
heatindex_json.add(roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals));
|
||||
heatindex_json.add(tempScale);
|
||||
dewpoint_json.add(roundf(sensorDewPoint * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals));
|
||||
dewpoint_json.add(tempScale);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Save Usermod Config Settings
|
||||
void addToConfig(JsonObject& root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name));
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top[F("I2CAddress")] = static_cast<uint8_t>(I2CAddress);
|
||||
top[F("TemperatureDecimals")] = TemperatureDecimals;
|
||||
top[F("HumidityDecimals")] = HumidityDecimals;
|
||||
top[F("PressureDecimals")] = PressureDecimals;
|
||||
top[F("TemperatureInterval")] = TemperatureInterval;
|
||||
top[F("PressureInterval")] = PressureInterval;
|
||||
top[F("PublishAlways")] = PublishAlways;
|
||||
top[F("UseCelsius")] = UseCelsius;
|
||||
top[F("HomeAssistantDiscovery")] = HomeAssistantDiscovery;
|
||||
DEBUG_PRINTLN(F("BME280 config saved."));
|
||||
}
|
||||
|
||||
// Read Usermod Config Settings
|
||||
bool readFromConfig(JsonObject& root)
|
||||
{
|
||||
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor
|
||||
// setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
|
||||
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
if (top.isNull()) {
|
||||
DEBUG_PRINT(F(_name));
|
||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
||||
return false;
|
||||
}
|
||||
bool configComplete = !top.isNull();
|
||||
|
||||
configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled);
|
||||
// A 3-argument getJsonValue() assigns the 3rd argument as a default value if the Json value is missing
|
||||
uint8_t tmpI2cAddress;
|
||||
configComplete &= getJsonValue(top[F("I2CAddress")], tmpI2cAddress, 0x76);
|
||||
I2CAddress = static_cast<BME280I2C::I2CAddr>(tmpI2cAddress);
|
||||
|
||||
configComplete &= getJsonValue(top[F("TemperatureDecimals")], TemperatureDecimals, 1);
|
||||
configComplete &= getJsonValue(top[F("HumidityDecimals")], HumidityDecimals, 0);
|
||||
configComplete &= getJsonValue(top[F("PressureDecimals")], PressureDecimals, 0);
|
||||
configComplete &= getJsonValue(top[F("TemperatureInterval")], TemperatureInterval, 30);
|
||||
configComplete &= getJsonValue(top[F("PressureInterval")], PressureInterval, 30);
|
||||
configComplete &= getJsonValue(top[F("PublishAlways")], PublishAlways, false);
|
||||
configComplete &= getJsonValue(top[F("UseCelsius")], UseCelsius, true);
|
||||
configComplete &= getJsonValue(top[F("HomeAssistantDiscovery")], HomeAssistantDiscovery, false);
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (!initDone) {
|
||||
// first run: reading from cfg.json
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
// changing parameters from settings page
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
|
||||
// Reset all known values
|
||||
sensorType = 0;
|
||||
sensorTemperature = 0;
|
||||
sensorHumidity = 0;
|
||||
sensorHeatIndex = 0;
|
||||
sensorDewPoint = 0;
|
||||
sensorPressure = 0;
|
||||
lastTemperature = 0;
|
||||
lastHumidity = 0;
|
||||
lastHeatIndex = 0;
|
||||
lastDewPoint = 0;
|
||||
lastPressure = 0;
|
||||
|
||||
initializeBmeComms();
|
||||
}
|
||||
|
||||
return configComplete;
|
||||
}
|
||||
|
||||
uint16_t getId() {
|
||||
return USERMOD_ID_BME280;
|
||||
}
|
||||
};
|
||||
|
||||
const char UsermodBME280::_name[] PROGMEM = "BME280/BMP280";
|
||||
const char UsermodBME280::_enabled[] PROGMEM = "enabled";
|
||||
|
||||
|
||||
static UsermodBME280 bme280_v2;
|
||||
REGISTER_USERMOD(bme280_v2);
|
84
usermods/BME280_v2/README.md
Normal file
@ -0,0 +1,84 @@
|
||||
# Usermod BME280
|
||||
This Usermod is designed to read a `BME280` or `BMP280` sensor and output the following:
|
||||
- Temperature
|
||||
- Humidity (`BME280` only)
|
||||
- Pressure
|
||||
- Heat Index (`BME280` only)
|
||||
- Dew Point (`BME280` only)
|
||||
|
||||
Configuration is performed via the Usermod menu. There are no parameters to set in code! The following settings can be configured in the Usermod Menu:
|
||||
- The i2c address in decimal. Set it to either 118 (0x76, the default) or 119 (0x77).
|
||||
- Temperature Decimals (number of decimal places to output)
|
||||
- Humidity Decimals
|
||||
- Pressure Decimals
|
||||
- Temperature Interval (how many seconds between temperature and humidity measurements)
|
||||
- Pressure Interval
|
||||
- Publish Always (turn off to only publish changes, on to publish whether or not value changed)
|
||||
- Use Celsius (turn off to use Fahrenheit)
|
||||
- Home Assistant Discovery (turn on to sent MQTT Discovery entries for Home Assistant)
|
||||
- SCL/SDA GPIO Pins
|
||||
|
||||
Dependencies
|
||||
- Libraries
|
||||
- `BME280@~3.0.0` (by [finitespace](https://github.com/finitespace/BME280))
|
||||
- `Wire`
|
||||
- Data is published over MQTT - make sure you've enabled the MQTT sync interface.
|
||||
- This usermod also writes to serial (GPIO1 on ESP8266). Please make sure nothing else is listening to the serial TX pin or your board will get confused by log messages!
|
||||
|
||||
In addition to outputting via MQTT, you can read the values from the Info Screen on the dashboard page of the device's web interface.
|
||||
|
||||
Methods also exist to read the read/calculated values from other WLED modules through code.
|
||||
- `getTemperatureC()`
|
||||
- `getTemperatureF()`
|
||||
- `getHumidity()`
|
||||
- `getPressure()`
|
||||
- `getDewPointC()`
|
||||
- `getDewPointF()`
|
||||
- `getHeatIndexC()`
|
||||
- `getHeatIndexF()`
|
||||
|
||||
# Compiling
|
||||
|
||||
To enable, add `BME280_v2` to your `custom_usermods` (e.g. in `platformio_override.ini`)
|
||||
```ini
|
||||
[env:usermod_bme280_d1_mini]
|
||||
extends = env:d1_mini
|
||||
custom_usermods = ${env:d1_mini.custom_usermods} BME280_v2
|
||||
```
|
||||
|
||||
|
||||
# MQTT
|
||||
MQTT topics are as follows (`<deviceTopic>` is set in MQTT section of Sync Setup menu):
|
||||
Measurement type | MQTT topic
|
||||
--- | ---
|
||||
Temperature | `<deviceTopic>/temperature`
|
||||
Humidity | `<deviceTopic>/humidity`
|
||||
Pressure | `<deviceTopic>/pressure`
|
||||
Heat index | `<deviceTopic>/heat_index`
|
||||
Dew point | `<deviceTopic>/dew_point`
|
||||
|
||||
If you are using Home Assistant, and `Home Assistant Discovery` is turned on, Home Assistant should automatically detect a new device, provided you have the MQTT integration installed. The device is separate from the main WLED device and will contain sensors for Pressure, Humidity, Temperature, Dew Point and Heat Index.
|
||||
|
||||
# Revision History
|
||||
Jul 2022
|
||||
- Added Home Assistant Discovery
|
||||
- Added API interface to output data
|
||||
- Removed compile-time variables
|
||||
- Added usermod menu interface
|
||||
- Added value outputs to info screen
|
||||
- Updated `readme.md`
|
||||
- Registered usermod
|
||||
- Implemented PinManager for usermod
|
||||
- Implemented reallocation of pins without reboot
|
||||
|
||||
Apr 2021
|
||||
- Added `Publish Always` option
|
||||
|
||||
Dec 2020
|
||||
- Ported to V2 Usermod format
|
||||
- Customizable `measure intervals`
|
||||
- Customizable number of `decimal places` in published sensor values
|
||||
- Pressure measured in units of hPa instead of Pa
|
||||
- Calculation of heat index (apparent temperature) and dew point
|
||||
- `16x oversampling` of sensor during measurement
|
||||
- Values only published if they are different from the previous value
|
6
usermods/BME280_v2/library.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name:": "BME280_v2",
|
||||
"dependencies": {
|
||||
"finitespace/BME280":"~3.0.0"
|
||||
}
|
||||
}
|
BIN
usermods/BME68X_v2/BME680.pdf
Normal file
1114
usermods/BME68X_v2/BME68X_v2.cpp
Normal file
163
usermods/BME68X_v2/README.md
Normal file
@ -0,0 +1,163 @@
|
||||
# Usermod BME68X
|
||||
|
||||
This usermod was developed for a BME680/BME68X sensor. The BME68X is not compatible with the BME280/BMP280 chip. It has its own library. The original 'BSEC Software Library' from Bosch was used to develop the code. The measured values are displayed on the WLED info page.
|
||||
|
||||
<p align="center"><img src="pics/pic1.png" style="width:60%;"></p>
|
||||
|
||||
In addition, the values are published on MQTT if this is active. The topic used for this is: 'wled/[MQTT Client ID]'. The Client ID is set in the WLED MQTT settings.
|
||||
|
||||
<p align="center"><img src="pics/pic2.png"></p>
|
||||
|
||||
If you use HomeAssistance discovery, the device tree for HomeAssistance is created. This is published under the topic 'homeassistant/sensor/[MQTT Client ID]' via MQTT.
|
||||
|
||||
<p align="center"><img src="pics/pic3.png"></p>
|
||||
|
||||
A device with the following sensors appears in HomeAssistant. Please note that MQTT must be activated in HomeAssistant.
|
||||
|
||||
<p align="center"><img src="pics/pic4.png" style="width:60%;"></p>
|
||||
|
||||
## Features
|
||||
|
||||
Raw sensor types
|
||||
|
||||
Sensor Accuracy Scale Range
|
||||
-----------------------------
|
||||
|
||||
Temperature +/- 1.0 °C/°F -40 to 85 °C
|
||||
Humidity +/- 3 % 0 to 100 %
|
||||
Pressure +/- 1 hPa 300 to 1100 hPa
|
||||
Gas Resistance Ohm
|
||||
The BSEC Library calculates the following values via the gas resistance
|
||||
|
||||
Sensor Accuracy Scale Range
|
||||
-----------------------------
|
||||
|
||||
IAQ value between 0 and 500
|
||||
Static IAQ same as IAQ but for permanently installed devices
|
||||
CO2 PPM
|
||||
VOC PPM
|
||||
Gas-Percentage %
|
||||
In addition the usermod calculates
|
||||
|
||||
Sensor Accuracy Scale Range
|
||||
-----------------------------
|
||||
|
||||
Absolute humidity g/m³
|
||||
Dew point °C/°F
|
||||
|
||||
### IAQ (Indoor Air Quality)
|
||||
|
||||
The IAQ is divided into the following value groups.
|
||||
|
||||
<p align="center"><img src="pics/pic5.png"></p>
|
||||
|
||||
For more detailed information, please consult the enclosed Bosch product description (BME680.pdf).
|
||||
|
||||
## Calibration of the device
|
||||
|
||||
The gas sensor of the BME68X must be calibrated. This differs from the BME280, which does not require any calibration.
|
||||
There is a range of additional information for this, which the driver also provides. These values can be found in HomeAssistant under Diagnostics.
|
||||
|
||||
- **STABILIZATION_STATUS**: Gas sensor stabilization status [boolean] Indicates initial stabilization status of the gas sensor element: stabilization is ongoing (0) or stabilization is finished (1).
|
||||
- **RUN_IN_STATUS**: Gas sensor run-in status [boolean] Indicates power-on stabilization status of the gas sensor element: stabilization is ongoing (0) or stabilization is finished (1)
|
||||
|
||||
Furthermore, all GAS based values have their own accuracy value. These have the following meaning:
|
||||
|
||||
- **Accuracy = 0** means the sensor is being stabilized (this can take a while on the first run)
|
||||
- **Accuracy = 1** means that the previous measured values show too few differences and cannot be used for calibration. If the sensor is at accuracy 1 for too long, you must ensure that the ambient air is chaning. Opening the windows is fine. Or sometimes it is sufficient to breathe on the sensor for approx. 5 minutes.
|
||||
- **Accuracy = 2** means the sensor is currently calibrating.
|
||||
- **Accuracy = 3** means that the sensor has been successfully calibrated. Once accuracy 3 is reached, the calibration data is automatically written to the file system. This calibration data will be used again at the next start and will speed up the calibration.
|
||||
|
||||
The IAQ index is therefore only meaningful if IAQ Accuracy = 3. In addition to the value for IAQ, BSEC also provides us with CO2 and VOC equivalent values. When using the sensor, the calibration value should also always be read out and displayed or transmitted.
|
||||
|
||||
Reasonably reliable values are therefore only achieved when accuracy displays the value 3.
|
||||
|
||||
## Settings
|
||||
|
||||
The settings of the usermods are set in the usermod section of wled.
|
||||
|
||||
<p align="center"><img src="pics/pic6.png"></p>
|
||||
|
||||
The possible settings are
|
||||
|
||||
- **Enable:** Enables / disables the usermod
|
||||
- **I2C address:** I2C address of the sensor. You can choose between 0X77 & 0X76. The default is 0x77.
|
||||
- **Interval:** Specifies the interval of seconds at which the usermod should be executed. The default is every second.
|
||||
- **Pub Chages Only:** If this item is active, the values are only published if they have changed since the last publication.
|
||||
- **Pub Accuracy:** The Accuracy values associated with the gas values are also published.
|
||||
- **Pub Calib State:** If this item is active, STABILIZATION_STATUS& RUN_IN_STATUS are also published.
|
||||
- **Temp Scale:** Here you can choose between °C and °F.
|
||||
- **Temp Offset:** The temperature offset is always set in °C. It must be converted for Fahrenheit.
|
||||
- **HA Discovery:** If this item is active, the HomeAssistant sensor tree is created.
|
||||
- **Pause While WLED Active:** If WLED has many LEDs to calculate, the computing power may no longer be sufficient to calculate the LEDs and read the sensor data. The LEDs then hang for a few microseconds, which can be seen. If this point is active, no sensor data is fetched as long as WLED is running.
|
||||
- **Del Calibration Hist:** If a check mark is set here, the calibration file saved in the file system is deleted when the settings are saved.
|
||||
|
||||
### Sensors
|
||||
|
||||
Applies to all sensors. The number of decimal places is set here. If the sensor is set to -1, it will no longer be published. In addition, the IAQ values can be activated here in verbal form.
|
||||
|
||||
It is recommended to use the Static IAQ for the IAQ values. This is recommended by Bosch for statically placed devices.
|
||||
|
||||
## Output
|
||||
|
||||
Data is published over MQTT - make sure you've enabled the MQTT sync interface.
|
||||
|
||||
In addition to outputting via MQTT, you can read the values from the Info Screen on the dashboard page of the device's web interface.
|
||||
|
||||
Methods also exist to read the read/calculated values from other WLED modules through code.
|
||||
|
||||
- getTemperature(); The scale °C/°F is depended to the settings
|
||||
- getHumidity();
|
||||
- getPressure();
|
||||
- getGasResistance();
|
||||
- getAbsoluteHumidity();
|
||||
- getDewPoint(); The scale °C/°F is depended to the settings
|
||||
- getIaq();
|
||||
- getStaticIaq();
|
||||
- getCo2();
|
||||
- getVoc();
|
||||
- getGasPerc();
|
||||
- getIaqAccuracy();
|
||||
- getStaticIaqAccuracy();
|
||||
- getCo2Accuracy();
|
||||
- getVocAccuracy();
|
||||
- getGasPercAccuracy();
|
||||
- getStabStatus();
|
||||
- getRunInStatus();
|
||||
|
||||
## Compilation
|
||||
|
||||
To enable, compile with `BME68X` in `custom_usermods` (e.g. in `platformio_override.ini`)
|
||||
|
||||
Example:
|
||||
|
||||
```[env:esp32_mySpecial]
|
||||
extends = env:esp32dev
|
||||
custom_usermods = ${env:esp32dev.custom_usermods} BME68X
|
||||
```
|
||||
|
||||
## Revision History
|
||||
|
||||
### Version 1.0.0
|
||||
|
||||
- First version of the BME68X_v user module
|
||||
|
||||
### Version 1.0.1
|
||||
|
||||
- Rebased to WELD Version 0.15
|
||||
- Reworked some default settings
|
||||
- A problem with the default settings has been fixed
|
||||
|
||||
### Version 1.0.2
|
||||
|
||||
* Rebased to WELD Version 0.16
|
||||
* Fixed: Solved compilation problems related to some macro naming interferences.
|
||||
|
||||
## Known problems
|
||||
|
||||
- MQTT goes online at device start. Shortly afterwards it goes offline and takes quite a while until it goes online again. The problem does not come from this user module, but from the WLED core.
|
||||
- If you save the settings often, WLED can get stuck.
|
||||
- If many LEDS are connected to WLED, reading the sensor can cause a small but noticeable hang. The "Pause While WLED Active" option was introduced as a workaround.
|
||||
|
||||
<div><img src="pics/GeoGab.svg" width="20%"/> </div>
|
||||
Gabriel Sieben (gsieben@geogab.net)
|
6
usermods/BME68X_v2/library.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name:": "BME68X",
|
||||
"dependencies": {
|
||||
"boschsensortec/BSEC Software Library":"^1.8.1492"
|
||||
}
|
||||
}
|
76
usermods/BME68X_v2/pics/GeoGab.svg
Normal file
@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xml:space="preserve"
|
||||
style="enable-background:new 0 0 595.28 127.56;"
|
||||
viewBox="0 0 600 135"
|
||||
y="0px"
|
||||
x="0px"
|
||||
id="Layer_1"
|
||||
version="1.1"><metadata
|
||||
id="metadata2372"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs2370"><linearGradient
|
||||
osb:paint="solid"
|
||||
id="linearGradient3877"><stop
|
||||
id="stop3875"
|
||||
offset="0"
|
||||
style="stop-color:#808285;stop-opacity:1;" /></linearGradient><clipPath
|
||||
id="clipPath2379"
|
||||
clipPathUnits="userSpaceOnUse"><g
|
||||
style="fill:#808285;fill-opacity:1;fill-rule:nonzero;stroke:#230065;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.99607843"
|
||||
id="use2381"><path
|
||||
style="fill:#808285;fill-opacity:1;fill-rule:nonzero;stroke:#230065;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.99607843"
|
||||
class="st0"
|
||||
d="m 588.34,94.29 c 0,13.28 -10.77,24.04 -24.04,24.04 H 31.38 C 18.1,118.33 7.34,107.57 7.34,94.29 V 32.38 C 7.34,19.1 18.1,8.34 31.38,8.34 h 532.91 c 13.28,0 24.04,10.76 24.04,24.04 V 94.29 Z M 159.44,69.66 c 0,17.15 11.68,19.07 21.89,19.07 h 14.23 c 14.6,0 23.44,-3.2 23.44,-13.87 v -0.91 h -7.84 c -0.45,7.11 -4.47,8.39 -14.32,8.39 h -10.76 c -13.68,0 -18.79,-2.74 -18.79,-13.32 V 66.65 H 219 V 60.9 C 219,44.3 208.88,40.74 194.83,40.74 h -12.59 c -11.12,0 -22.8,1.92 -22.8,18.79 z m 51.72,-9.95 h -43.87 c 0.73,-10.58 3.65,-12.59 16.05,-12.59 h 11.49 c 12.77,0 16.33,4.74 16.33,9.49 z m 18.62,10.13 c 0,15.33 10.86,18.88 25.73,18.88 h 11.22 c 14.87,0 25.72,-3.56 25.72,-18.88 V 59.62 c 0,-15.32 -10.85,-18.88 -25.72,-18.88 h -11.22 c -14.87,0 -25.73,3.56 -25.73,18.88 z m 54.82,-0.46 c 0,10.86 -6.38,12.95 -15.51,12.95 h -15.96 c -9.12,0 -15.51,-2.09 -15.51,-12.95 v -9.31 c 0,-10.85 6.39,-12.95 15.51,-12.95 h 15.96 c 9.12,0 15.51,2.1 15.51,12.95 z m 173.79,18.61 v -31.2 c 0,-13.87 -9.85,-16.06 -24.45,-16.06 h -12.41 c -14.05,0 -19.43,3.47 -19.43,11.95 v 2.28 h 7.84 v -1.64 c 0,-3.83 1.92,-6.2 11.77,-6.2 h 11.49 c 12.04,0 17.33,1.09 17.33,9.49 v 8.76 h -0.18 c -2.74,-4.47 -6.39,-5.56 -16.6,-5.56 H 421.16 C 407.48,59.81 400,61.09 400,71.67 v 3.1 c 0,8.76 3.1,13.96 14.96,13.96 h 18.79 z m -7.84,-12.5 c 0,5.84 -5.47,6.84 -19.7,6.84 h -10.4 c -10.76,0 -12.59,-2.19 -12.59,-7.39 v -1.46 c 0,-5.84 2.83,-7.3 12.95,-7.3 h 12.04 c 12.04,0 17.7,0.82 17.7,7.2 z M 73.6,66.69 h 10.88 l 5.03,0.01 h 48.67 v 2.41 c 0,9.12 -5.47,12.23 -14.41,12.23 H 88.83 c -8.58,0 -15.51,-2.55 -15.51,-14.05 v -0.35 -0.26 z m -9.16,-0.01 c 0,0.06 0,0.14 0,0.2 0.67,14.68 6.68,21.76 23.47,21.76 h 36.85 c 16.51,0 22.35,-6.39 22.35,-24.36 V 59.4 H 89.51 L 85.39,59.39 H 73.32 v -0.01 -16.9 c 0,-11.49 6.93,-14.05 15.51,-14.05 H 112 c 22.07,0 25.81,0.91 25.81,13.23 h 8.39 v -2.37 c 0,-15.23 -12.68,-18.15 -24.54,-18.15 H 87.92 c -17.88,0 -23.53,8.03 -23.53,24.72 V 59.39 L 64.38,59.4 H 44.6 c -11.86,0 -24.54,2.92 -24.54,18.15 v 2.37 5.02 2.37 c 0,15.24 12.68,18.16 24.54,18.16 h 40.79 432.59 34.03 c 17.88,0 23.54,-8.03 23.54,-24.72 V 45.88 c 0,-16.69 -5.65,-24.72 -23.54,-24.72 l -0.16,-0.02 h -223 c -17.88,0 -23.53,8.03 -23.53,24.72 v 18.06 c 0,16.69 5.65,24.72 23.53,24.72 h 36.85 c 16.51,0 22.35,-6.39 22.35,-24.36 V 59.4 h -42.78 v 7.3 h 33.84 v 2.41 c 0,9.12 -5.47,12.23 -14.41,12.23 h -34.94 c -8.58,0 -15.5,-2.55 -15.5,-14.05 V 42.48 c 0,-11.49 6.93,-14.05 15.5,-14.05 l 142.5,0.02 v 61.24 l 24.35,0.73 h 10.95 c 14.23,0 24.18,-3.56 24.18,-19.61 V 62.6 c 0,-14.87 -5.75,-20.16 -24.63,-20.16 h -11.95 -14.88 -0.17 l -0.02,-14.01 h 71.75 l -0.75,0.02 c 8.58,0 15.51,2.55 15.51,14.04 v 41.62 c 0,11.49 -6.93,14.05 -15.51,14.05 H 518.97 86.87 54.27 c -22.08,0 -25.81,-0.91 -25.81,-13.23 v -5.02 c 0,-12.31 3.74,-13.23 25.81,-13.23 h 9.84 z m 459.45,4.04 c 0,9.03 -2.83,13.32 -15.6,13.32 h -12.77 c -11.67,0 -15.42,-4.93 -15.42,-13.41 v -8.85 c 0,-11.13 6.48,-12.95 17.06,-12.95 h 10.58 c 10.76,0 16.14,2.01 16.14,12.77 v 9.12 z"
|
||||
id="path3935" /><path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#230065;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.99607843"
|
||||
class="st0"
|
||||
d="m 611.07375,-48.387188 c 0,13.28 -10.77,24.039999 -24.04,24.039999 H 54.113747 c -13.27999,0 -24.04,-10.76 -24.04,-24.039999 v -61.910002 c 0,-13.28 10.76001,-24.04 24.04,-24.04 H 587.02375 c 13.28,0 24.04,10.76 24.04,24.04 v 61.910002 z"
|
||||
id="path3937" /></g></clipPath><clipPath
|
||||
id="clipPath2398"
|
||||
clipPathUnits="userSpaceOnUse"><g
|
||||
id="use2400"
|
||||
style="fill:#268298;fill-opacity:1"><g
|
||||
id="g3959"
|
||||
clip-path="url(#clipPath2407)"
|
||||
style="fill:#268298;fill-opacity:1"><g
|
||||
id="g3957"
|
||||
style="fill:#268298;fill-opacity:1"><rect
|
||||
style="opacity:1;fill:#268298;fill-opacity:1;stroke:#000000;stroke-opacity:1"
|
||||
id="rect3955"
|
||||
width="350.98587"
|
||||
height="147.57361"
|
||||
x="95.224861"
|
||||
y="-97.290329" /></g></g></g></clipPath><clipPath
|
||||
id="clipPath2407"
|
||||
clipPathUnits="userSpaceOnUse"><g
|
||||
id="use2409"
|
||||
style="fill:#268298;fill-opacity:1"><rect
|
||||
style="opacity:1;fill:#268298;fill-opacity:1;stroke:#000000;stroke-opacity:1"
|
||||
id="rect3963"
|
||||
width="350.98587"
|
||||
height="147.57361"
|
||||
x="95.224861"
|
||||
y="-97.290329" /></g></clipPath></defs>
|
||||
<style
|
||||
id="style2363"
|
||||
type="text/css">
|
||||
.st0{fill:#808285;}
|
||||
</style>
|
||||
<path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:0.9859813;fill-rule:nonzero;stroke:#5c7823;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.99607843"
|
||||
class="st0"
|
||||
d="M 600,103.51782 C 600,116.79902 588.8778,127.56 575.17384,127.56 H 24.826163 C 11.111876,127.56 5.1635108e-7,116.79902 5.1635108e-7,103.51782 V 41.602186 C 5.1635108e-7,28.320979 11.111876,17.560001 24.826163,17.560001 H 575.16351 c 13.71429,0 24.82616,10.760978 24.82616,24.042185 v 61.915634 z"
|
||||
id="path2365-0" /><path
|
||||
style="fill:#808285;fill-opacity:1;fill-rule:nonzero;stroke:#230065;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.99607843"
|
||||
class="st0"
|
||||
d="M 600,103.51782 C 600,116.79902 588.8778,127.56 575.17384,127.56 H 24.826162 C 11.111877,127.56 5.1635111e-7,116.79902 5.1635111e-7,103.51782 V 41.602187 C 5.1635111e-7,28.320977 11.111876,17.559997 24.826162,17.559997 H 575.16351 c 13.71429,0 24.82616,10.76098 24.82616,24.04219 V 103.51782 Z M 157.07401,78.885567 c 0,17.15156 12.06196,19.07173 22.60585,19.07173 h 14.69535 c 15.07745,0 24.20654,-3.20029 24.20654,-13.87125 v -0.91009 h -8.09638 c -0.46472,7.11065 -4.61618,8.39076 -14.7883,8.39076 h -11.11188 c -14.12736,0 -19.40447,-2.74024 -19.40447,-13.3212 v -2.37022 h 53.40103 v -5.75052 c 0,-16.60151 -10.45094,-20.16183 -24.96041,-20.16183 h -13.00172 c -11.48365,0 -23.54561,1.92017 -23.54561,18.79171 z m 53.41136,-9.9509 h -45.30465 c 0.75387,-10.58096 3.76936,-12.59114 16.57487,-12.59114 h 11.86575 c 13.18761,0 16.86403,4.74043 16.86403,9.49086 z m 19.22891,10.13092 c 0,15.33139 11.21515,18.88171 26.57143,18.88171 h 11.58692 c 15.35629,0 26.56111,-3.56032 26.56111,-18.88171 v -10.22093 c 0,-15.32139 -11.20482,-18.88171 -26.56111,-18.88171 h -11.58692 c -15.35628,0 -26.57143,3.56032 -26.57143,18.88171 z m 56.61275,-0.46004 c 0,10.86098 -6.58865,12.95117 -16.01722,12.95117 h -16.48193 c -9.41825,0 -16.01721,-2.09019 -16.01721,-12.95117 v -9.31085 c 0,-10.85098 6.59896,-12.95117 16.01721,-12.95117 h 16.48193 c 9.41824,0 16.01722,2.10019 16.01722,12.95117 z m 179.47332,18.61169 v -31.20283 c 0,-13.87126 -10.17212,-16.06146 -25.24957,-16.06146 h -12.81584 c -14.50946,0 -20.0654,3.47031 -20.0654,11.95108 v 2.28021 h 8.09638 v -1.64015 c 0,-3.83035 1.98279,-6.20056 12.15491,-6.20056 h 11.86575 c 12.43373,0 17.89673,1.0901 17.89673,9.49086 v 8.7608 h -0.18589 c -2.8296,-4.47041 -6.59897,-5.56051 -17.14286,-5.56051 h -13.00172 c -14.12736,0 -21.85198,1.28012 -21.85198,11.86108 v 3.10028 c 0,8.76079 3.20138,13.96126 15.44923,13.96126 h 19.40447 z m -8.09639,-12.50114 c 0,5.84053 -5.64888,6.84062 -20.34423,6.84062 h -10.74011 c -11.11187,0 -13.00172,-2.19019 -13.00172,-7.39067 v -1.46013 c 0,-5.84053 2.92255,-7.30066 13.3735,-7.30066 h 12.43373 c 12.43374,0 18.27883,0.82007 18.27883,7.20065 z m -389.27711,-8.8008 h 11.2358 l 5.194492,0.01 h 50.261618 v 2.41021 c 0,9.12083 -5.64888,12.23111 -14.88124,12.23111 H 84.154905 c -8.860585,0 -16.017212,-2.55023 -16.017212,-14.05127 v -0.35003 -0.26003 z m -9.459552,-0.01 c 0,0.06 0,0.14002 0,0.20002 0.69191,14.68133 6.898451,21.76198 24.237521,21.76198 h 38.055071 c 17.04992,0 23.0809,-6.39058 23.0809,-24.36221 v -4.88045 H 84.857142 l -4.254733,-0.01 H 68.137693 v -0.01 -16.90153 c 0,-11.49104 7.156627,-14.05129 16.017212,-14.05129 h 23.927705 c 22.79174,0 26.65405,0.91009 26.65405,13.23121 h 8.66437 v -2.37021 c 0,-15.23139 -13.09466,-18.15166 -25.34251,-18.15166 H 83.215146 c -18.464717,0 -24.299483,8.03073 -24.299483,24.72226 v 13.53122 l -0.01033,0.01 h -20.42685 c -12.247849,0 -25.342513,2.92027 -25.342513,18.15165 v 2.37022 5.02045 2.37022 c 0,15.241393 13.094664,18.161653 25.342513,18.161653 h 42.123923 446.736664 35.14286 c 18.46471,0 24.30981,-8.03073 24.30981,-24.722253 v -34.87316 c 0,-16.69153 -5.83477,-24.72226 -24.30981,-24.72226 l -0.16523,-0.02 H 332.0241 c -18.46471,0 -24.29948,8.03073 -24.29948,24.72226 v 18.06163 c 0,16.69152 5.83477,24.72225 24.29948,24.72225 h 38.05508 c 17.04991,0 23.08089,-6.39058 23.08089,-24.36221 v -4.88045 h -44.179 v 7.30067 h 34.94664 v 2.41021 c 0,9.12083 -5.64888,12.23111 -14.88123,12.23111 h -36.08262 c -8.86059,0 -16.00688,-2.55023 -16.00688,-14.05127 v -24.81225 c 0,-11.49104 7.15662,-14.05129 16.00688,-14.05129 l 147.16007,0.02 v 61.24556 l 25.14629,0.73007 h 11.30809 c 14.69536,0 24.97074,-3.56033 24.97074,-19.61178 v -8.21075 c 0,-14.87135 -5.93803,-20.16183 -25.43545,-20.16183 h -12.34079 -15.36661 -0.17556 l -0.0207,-14.01128 h 74.09639 l -0.77453,0.02 c 8.86059,0 16.01721,2.55024 16.01721,14.04128 v 41.62378 c 0,11.491053 -7.15662,14.051283 -16.01721,14.051283 H 528.36145 82.130808 48.464716 c -22.802065,0 -26.654044,-0.91008 -26.654044,-13.231213 v -5.02046 c 0,-12.31111 3.862306,-13.2312 26.654044,-13.2312 h 10.161789 z m 474.475042,4.04037 c 0,9.03082 -2.92255,13.32121 -16.11015,13.32121 h -13.18761 c -12.05164,0 -15.92427,-4.93045 -15.92427,-13.41122 v -8.8508 c 0,-11.13101 6.69191,-12.95118 17.6179,-12.95118 h 10.92599 c 11.11188,0 16.66781,2.01019 16.66781,12.77116 v 9.12083 z"
|
||||
id="path2365" />
|
||||
</svg>
|
After Width: | Height: | Size: 11 KiB |
BIN
usermods/BME68X_v2/pics/pic1.png
Normal file
After Width: | Height: | Size: 135 KiB |
BIN
usermods/BME68X_v2/pics/pic2.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
usermods/BME68X_v2/pics/pic3.png
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
usermods/BME68X_v2/pics/pic4.png
Normal file
After Width: | Height: | Size: 134 KiB |
BIN
usermods/BME68X_v2/pics/pic5.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
usermods/BME68X_v2/pics/pic6.png
Normal file
After Width: | Height: | Size: 35 KiB |