Compare commits
55 Commits
31081d98ee
...
main
Author | SHA1 | Date | |
---|---|---|---|
fc080f7796 | |||
70fe5a0cdc | |||
2a7b5527a5 | |||
50545e3170 | |||
d2826a0f63 | |||
87fc74bb51 | |||
03f3f02da8 | |||
524db5e979 | |||
279416cded | |||
fbd14f2e16 | |||
1989f6f5c9 | |||
a19b1e86f2 | |||
c63e907204 | |||
b7920e224f | |||
42e92dafc8 | |||
0b6eb9724f | |||
55ef5c1580 | |||
c15f9787a7 | |||
3d0078f118 | |||
9e72dba035 | |||
3d7dd754eb | |||
2dd20fa51b | |||
d33bd6b0e4 | |||
8902adf18c | |||
9abd425f46 | |||
ee28b5805d | |||
ec29dbdd01 | |||
3fa9377438 | |||
ec049b52c0 | |||
a009ea85bc | |||
bd2e6e56cf | |||
37c7280a15 | |||
2f10d4cabd | |||
385dcffe68 | |||
fa0578349b | |||
4a36ff0da0 | |||
bd4046572c | |||
fdd299b063 | |||
a44ef2d0ad | |||
2d1208e223 | |||
67279a8f46 | |||
a52ac3df99 | |||
c4356cf354 | |||
0c219e0697 | |||
cee8c20176 | |||
135f6b06f8 | |||
0aa3803f20 | |||
ed3351a20b | |||
c8bcb85062 | |||
17d33d98e2 | |||
afaf4c02e4 | |||
e1e472b2e4 | |||
2f6704a346 | |||
60dddafc04 | |||
db94530f29 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
settings.json
|
||||
settings.json
|
||||
.venv
|
||||
|
5
Pipfile
5
Pipfile
@@ -7,8 +7,13 @@ name = "pypi"
|
||||
mpremote = "*"
|
||||
pyserial = "*"
|
||||
esptool = "*"
|
||||
watchfiles = "*"
|
||||
|
||||
[dev-packages]
|
||||
|
||||
[requires]
|
||||
python_version = "3.12"
|
||||
|
||||
[scripts]
|
||||
|
||||
dev = 'watchfiles "./dev.py /dev/ttyACM0 src reset follow"'
|
572
Pipfile.lock
generated
572
Pipfile.lock
generated
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "8b14bb293b7e7117ffc89c2bc92d7aa2290e8f68be7fc0f073f2b3f7f959ef71"
|
||||
"sha256": "7b8033c15743e27f2589635c75bd0bb86ffc3a725b179d7db9ef200119aa9164"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@@ -16,163 +16,160 @@
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"argcomplete": {
|
||||
"anyio": {
|
||||
"hashes": [
|
||||
"sha256:2ab2c4a215c59fd6caaff41a869480a23e8f6a5f910b266c1808037f4e375b61",
|
||||
"sha256:c12bf50eded8aebb298c7b7da7a5ff3ee24dffd9f5281867dfe1424b58c55392"
|
||||
"sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6",
|
||||
"sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1"
|
||||
],
|
||||
"markers": "sys_platform != 'win32'",
|
||||
"version": "==3.5.3"
|
||||
"markers": "python_version >= '3.9'",
|
||||
"version": "==4.10.0"
|
||||
},
|
||||
"bitarray": {
|
||||
"hashes": [
|
||||
"sha256:000df24c183011b5d27c23d79970f49b6762e5bb5aacd25da9c3e9695c693222",
|
||||
"sha256:0027b8f3bb2bba914c79115e96a59b9924aafa1a578223a7c4f0a7242d349842",
|
||||
"sha256:00f9a88c56e373009ac3c73c55205cfbd9683fbd247e2f9a64bae3da78795252",
|
||||
"sha256:041c889e69c847b8a96346650e50f728b747ae176889199c49a3f31ae1de0e23",
|
||||
"sha256:0879f839ec8f079fa60c3255966c2e1aa7196699a234d4e5b7898fbc321901b5",
|
||||
"sha256:0b555006a7dea53f6bebc616a4d0249cecbf8f1fadf77860120a2e5dbdc2f167",
|
||||
"sha256:0b655c3110e315219e266b2732609fddb0857bc69593de29f3c2ba74b7d3f51a",
|
||||
"sha256:0cecaf2981c9cd2054547f651537b4f4939f9fe225d3fc2b77324b597c124e40",
|
||||
"sha256:0e104f9399144fab6a892d379ba1bb4275e56272eb465059beef52a77b4e5ce6",
|
||||
"sha256:0ef5c787c8263c082a73219a69eb60a500e157a4ac69d1b8515ad836b0e71fb4",
|
||||
"sha256:12f19ede03e685c5c588ab5ed63167999295ffab5e1126c5fe97d12c0718c18f",
|
||||
"sha256:1414a7102a3c4986f241480544f5c99f5d32258fb9b85c9c04e84e48c490ab35",
|
||||
"sha256:147542299f458bdb177f798726e5f7d39ab8491de4182c3c6d9885ed275a3c2b",
|
||||
"sha256:150b7b29c36d9f1a24779aea723fdfc73d1c1c161dc0ea14990da27d4e947092",
|
||||
"sha256:153d7c416a70951dcfa73487af05d2f49c632e95602f1620cd9a651fa2033695",
|
||||
"sha256:184972c96e1c7e691be60c3792ca1a51dd22b7f25d96ebea502fe3c9b554f25d",
|
||||
"sha256:18abdce7ab5d2104437c39670821cba0b32fdb9b2da9e6d17a4ff295362bd9dc",
|
||||
"sha256:2055206ed653bee0b56628f6a4d248d53e5660228d355bbec0014bdfa27050ae",
|
||||
"sha256:20f30373f0af9cb583e4122348cefde93c82865dbcbccc4997108b3d575ece84",
|
||||
"sha256:22b00f65193fafb13aa644e16012c8b49e7d5cbb6bb72825105ff89aadaa01e3",
|
||||
"sha256:251cd5bd47f542893b2b61860eded54f34920ea47fd5bff038d85e7a2f7ae99b",
|
||||
"sha256:2855cc01ee370f7e6e3ec97eebe44b1453c83fb35080313145e2c8c3c5243afb",
|
||||
"sha256:2ac67b658fa5426503e9581a3fb44a26a3b346c1abd17105735f07db572195b3",
|
||||
"sha256:2d9fe3ee51afeb909b68f97e14c6539ace3f4faa99b21012e610bbe7315c388d",
|
||||
"sha256:2da91ab3633c66999c2a352f0ca9ae064f553e5fc0eca231d28e7e305b83e942",
|
||||
"sha256:2dad7ba2af80f9ec1dd988c3aca7992408ec0d0b4c215b65d353d95ab0070b10",
|
||||
"sha256:34fc13da3518f14825b239374734fce93c1a9299ed7b558c3ec1d659ec7e4c70",
|
||||
"sha256:369b6d457af94af901d632c7e625ca6caf0a7484110fc91c6290ce26bc4f1478",
|
||||
"sha256:37be5482b9df3105bad00fdf7dc65244e449b130867c3879c9db1db7d72e508b",
|
||||
"sha256:3963b80a68aedcd722a9978d261ae53cb9bb6a8129cc29790f0f10ce5aca287a",
|
||||
"sha256:39b38a3d45dac39d528c87b700b81dfd5e8dc8e9e1a102503336310ef837c3fd",
|
||||
"sha256:3cd565253889940b4ec4768d24f101d9fe111cad4606fdb203ea16f9797cf9ed",
|
||||
"sha256:3d47bc4ff9b0e1624d613563c6fa7b80aebe7863c56c3df5ab238bb7134e8755",
|
||||
"sha256:3fa5d8e4b28388b337face6ce4029be73585651a44866901513df44be9a491ab",
|
||||
"sha256:42bf1b222c698b467097f58b9f59dc850dfa694dde4e08237407a6a103757aa3",
|
||||
"sha256:43b6c7c4f4a7b80e86e24a76f4c6b9b67d03229ea16d7d403520616535c32196",
|
||||
"sha256:44c3e78b60070389b824d5a654afa1c893df723153c81904088d4922c3cfb6ac",
|
||||
"sha256:4683bff52f5a0fd523fb5d3138161ef87611e63968e1fcb6cf4b0c6a86970fe0",
|
||||
"sha256:47ccf9887bd595d4a0536f2310f0dcf89e17ab83b8befa7dc8727b8017120fda",
|
||||
"sha256:4800c91a14656789d2e67d9513359e23e8a534c8ee1482bb9b517a4cfc845200",
|
||||
"sha256:4817d73d995bd2b977d9cde6050be8d407791cf1f84c8047fa0bea88c1b815bc",
|
||||
"sha256:4839d3b64af51e4b8bb4a602563b98b9faeb34fd6c00ed23d7834e40a9d080fc",
|
||||
"sha256:4ac2027ca650a7302864ed2528220d6cc6921501b383e9917afc7a2424a1e36d",
|
||||
"sha256:4cb5702dd667f4bb10fed056ffdc4ddaae8193a52cd74cb2cdb54e71f4ef2dd1",
|
||||
"sha256:53e002ac1073ac70e323a7a4bfa9ab95e7e1a85c79160799e265563f342b1557",
|
||||
"sha256:545d36332de81e4742a845a80df89530ff193213a50b4cbef937ed5a44c0e5e5",
|
||||
"sha256:572a61fba7e3a710a8324771322fba8488d134034d349dcd036a7aef74723a80",
|
||||
"sha256:57d5ef854f8ec434f2ffd9ddcefc25a10848393fe2976e2be2c8c773cf5fef42",
|
||||
"sha256:5ddbf71a97ad1d6252e6e93d2d703b624d0a5b77c153b12f9ea87d83e1250e0c",
|
||||
"sha256:5fa4b4d9fa90124b33b251ef74e44e737021f253dc7a9174e1b39f097451f7ca",
|
||||
"sha256:628f93e9c2c23930bd1cfe21c634d6c84ec30f45f23e69aefe1fcd262186d7bb",
|
||||
"sha256:648e7ce794928e8d11343b5da8ecc5b910af75a82ea1a4264d5d0a55c3785faa",
|
||||
"sha256:656db7bdf1d81ec3b57b3cad7ec7276765964bcfd0eb81c5d1331f385298169c",
|
||||
"sha256:666e44b0458bb2894b64264a29f2cc7b5b2cbcc4c5e9cedfe1fdbde37a8e329a",
|
||||
"sha256:66a33a537e781eac3a352397ce6b07eedf3a8380ef4a804f8844f3f45e335544",
|
||||
"sha256:66d6134b7bb737b88f1d16478ad0927c571387f6054f4afa5557825a4c1b78e2",
|
||||
"sha256:67a0b56dd02f2713f6f52cacb3f251afd67c94c5f0748026d307d87a81a8e15c",
|
||||
"sha256:6c33129b49196aa7965ac0f16fcde7b6ad8614b606caf01669a0277cef1afe1d",
|
||||
"sha256:6d2a2ce73f9897268f58857ad6893a1a6680c5a6b28f79d21c7d33285a5ae646",
|
||||
"sha256:71ad0139c95c9acf4fb62e203b428f9906157b15eecf3f30dc10b55919225896",
|
||||
"sha256:7814c9924a0b30ecd401f02f082d8697fc5a5be3f8d407efa6e34531ff3c306a",
|
||||
"sha256:787db8da5e9e29be712f7a6bce153c7bc8697ccc2c38633e347bb9c82475d5c9",
|
||||
"sha256:7cb885c043000924554fe2124d13084c8fdae03aec52c4086915cd4cb87fe8be",
|
||||
"sha256:7cd021ada988e73d649289cee00428b75564c46d55fbdcb0e3402e504b0ae5ea",
|
||||
"sha256:7e51e7f8289bf6bb631e1ef2a8f5e9ca287985ff518fe666abbdfdb6a848cb26",
|
||||
"sha256:7e9eee03f187cef1e54a4545124109ee0afc84398628b4b32ebb4852b4a66393",
|
||||
"sha256:7edb83089acbf2c86c8002b96599071931dc4ea5e1513e08306f6f7df879a48b",
|
||||
"sha256:7f1c24be7519f16a47b7e2ad1a1ef73023d34d8cbe1a3a59b185fc14baabb132",
|
||||
"sha256:8330912be6cb8e2fbfe8eb69f82dee139d605730cadf8d50882103af9ac83bb4",
|
||||
"sha256:8a9eb510cde3fa78c2e302bece510bf5ed494ec40e6b082dec753d6e22d5d1b1",
|
||||
"sha256:8c9733d2ff9b7838ac04bf1048baea153174753e6a47312be14c83c6a395424b",
|
||||
"sha256:904c1d5e3bd24f0c0d37a582d2461312033c91436a6a4f3bdeeceb4bea4a899d",
|
||||
"sha256:928b8b6dfcd015e1a81334cfdac02815da2a2407854492a80cf8a3a922b04052",
|
||||
"sha256:9502c2230d59a4ace2fddfd770dad8e8b414cbd99517e7e56c55c20997c28b8d",
|
||||
"sha256:96cf0898f8060b2d3ae491762ae871b071212ded97ff9e1e3a5229e9fefe544c",
|
||||
"sha256:98a4070ddafabddaee70b2aa7cc6286cf73c37984169ab03af1782da2351059a",
|
||||
"sha256:9929051feeaf8d948cc0b1c9ce57748079a941a1a15c89f6014edf18adaade84",
|
||||
"sha256:996d1b83eb904589f40974538223eaed1ab0f62be8a5105c280b9bd849e685c4",
|
||||
"sha256:9c6e52005e91803eb4e08c0a08a481fb55ddce97f926bae1f6fa61b3396b5b61",
|
||||
"sha256:9e3727ab63dfb6bde00b281934e2212bb7529ea3006c0031a556a84d2268bea5",
|
||||
"sha256:a0255bd05ec7165e512c115423a5255a3f301417973d20a80fc5bfc3f3640bcb",
|
||||
"sha256:a2083dc20f0d828a7cdf7a16b20dae56aab0f43dc4f347a3b3039f6577992b03",
|
||||
"sha256:a3c36b2fcfebe15ad1c10a90c1d52a42bebe960adcbce340fef867203028fbe7",
|
||||
"sha256:a4f49ac31734fe654a68e2515c0da7f5bbdf2d52755ba09a42ac406f1f08c9d0",
|
||||
"sha256:a667ea05ba1ea81b722682276dbef1d36990f8908cf51e570099fd505a89f931",
|
||||
"sha256:a754c1464e7b946b1cac7300c582c6fba7d66e535cd1dab76d998ad285ac5a37",
|
||||
"sha256:a817ad70c1aff217530576b4f037dd9b539eb2926603354fcac605d824082ad1",
|
||||
"sha256:aa54c7e1da8cf4be0aab941ea284ec64033ede5d6de3fd47d75e77cafe986e9d",
|
||||
"sha256:ab37da66a8736ad5a75a58034180e92c41e864da0152b84e71fcc253a2f69cd4",
|
||||
"sha256:ac06dd72ee1e1b6e312504d06f75220b5894af1fb58f0c20643698f5122aea76",
|
||||
"sha256:aca0a9cd376beaccd9f504961de83e776dd209c2de5a4c78dc87a78edf61839b",
|
||||
"sha256:acc07211a59e2f245e9a06f28fa374d094fb0e71cf5366eef52abbb826ddc81e",
|
||||
"sha256:aef404d5400d95c6ec86664df9924bde667c8865f8e33c9b7bd79823d53b3e5d",
|
||||
"sha256:b1047999f1797c3ea7b7c85261649249c243308dcf3632840d076d18fa72f142",
|
||||
"sha256:b7d09ef06ba57bea646144c29764bf6b870fb3c5558ca098191e07b6a1d40bf7",
|
||||
"sha256:bcf0150ae0bcc4aa97bdfcb231b37bad1a59083c1b5012643b266012bf420e68",
|
||||
"sha256:bcf524a087b143ba736aebbb054bb399d49e77cf7c04ed24c728e411adc82bfa",
|
||||
"sha256:beeb79e476d19b91fd6a3439853e4e5ba1b3b475920fa40d62bde719c8af786f",
|
||||
"sha256:bf90aba4cff9e72e24ecdefe33bad608f147a23fa5c97790a5bab0e72fe62b6d",
|
||||
"sha256:c23286abba0cb509733c6ce8f4013cd951672c332b2e184dbefbd7331cd234c8",
|
||||
"sha256:c2945e0390d1329c585c584c6b6d78be017d9c6a1288f9c92006fe907f69cc28",
|
||||
"sha256:c756a92cf1c1abf01e56a4cc40cb89f0ff9147f2a0be5b557ec436a23ff464d8",
|
||||
"sha256:c9e9fef0754867d88e948ce8351c9fd7e507d8514e0f242fd67c907b9cdf98b3",
|
||||
"sha256:ca79f02a98cbda1472449d440592a2fe2ad96fe55515a0447fa8864a38017cf8",
|
||||
"sha256:cb7302dbcfcb676f0b66f15891f091d0233c4fc23e1d4b9dc9b9e958156e347f",
|
||||
"sha256:cb98d5b6eac4b2cf2a5a69f60a9c499844b8bea207059e9fc45c752436e6bb49",
|
||||
"sha256:cc83ea003dd75e9ade3291ef0585577dd5524aec0c8c99305c0aaa2a7570d6db",
|
||||
"sha256:ce249ed981f428a8b61538ca82d3875847733d579dd40084ab8246549160f8a4",
|
||||
"sha256:cf0cc2e91dd38122dec2e6541efa99aafb0a62e118179218181eff720b4b8153",
|
||||
"sha256:d1a199e6d7c3bad5ba9d0e4dc00dde70ee7d111c9dfc521247fa646ef59fa57e",
|
||||
"sha256:d1d5abf1d6d910599ac16afdd9a0ed3e24f3b46af57f3070cf2792f236f36e0b",
|
||||
"sha256:d3f761184b93092077c7f6b7dad7bd4e671c1620404a76620da7872ceb576a94",
|
||||
"sha256:d756bfeb62ca4fe65d2af7a39249d442c05070c047d03729ad6cd4c2e9b0f0bd",
|
||||
"sha256:d8c36ddc1923bcc4c11b9994c54eaae25034812a42400b7b8a86fe6d242166a2",
|
||||
"sha256:dbe1084935b942fab206e609fa1ed3f46ad1f2612fb4833e177e9b2a5e006c96",
|
||||
"sha256:dc1937a0ff2671797d35243db4b596329842480d125a65e9fe964bcffaf16dfc",
|
||||
"sha256:dfea514e665af278b2e1d4deb542de1cd4f77413bee83dd15ae16175976ea8d5",
|
||||
"sha256:e008b7b4ce6c7f7a54b250c45c28d4243cc2a3bbfd5298fa7dac92afda229842",
|
||||
"sha256:e0e7f24a0b01e6e6a0191c50b06ca8edfdec1988d9d2b264d669d2487f4f4680",
|
||||
"sha256:e15c94d79810c5ab90ddf4d943f71f14332890417be896ca253f21fa3d78d2b1",
|
||||
"sha256:e56ba8be5f17dee0ffa6d6ce85251e062ded2faa3cbd2558659c671e6c3bf96d",
|
||||
"sha256:e89ea59a3ed86a6eb150d016ed28b1bedf892802d0ed32b5659d3199440f3ced",
|
||||
"sha256:e91d46d12781a14ccb8b284566b14933de4e3b29f8bc5e1c17de7a2001ad3b5b",
|
||||
"sha256:ea40e98d751ed4b255db4a88fe8fb743374183f78470b9e9305aab186bf28ede",
|
||||
"sha256:eb27c01b747649afd7e1c342961680893df6d8d81f832a6f04d8c8e03a8a54cc",
|
||||
"sha256:ec5b0f2d13da53e0975ac15ecbe8badb463bdb0bebaa09457f4df3320421915c",
|
||||
"sha256:ee040ad3b7dfa05e459713099f16373c1f2a6f68b43cb0575a66718e7a5daef4",
|
||||
"sha256:f12cc7c7638074918cdcc7491aff897df921b092ffd877227892d2686e98f876",
|
||||
"sha256:f536fc4d1a683025f9caef0bebeafd60384054579ffe0825bb9bd8c59f8c55b8",
|
||||
"sha256:f71f24b58e75a889b9915e3197865302467f13e7390efdea5b6afc7424b3a2ea",
|
||||
"sha256:f75fc0198c955d840b836059bd43e0993edbf119923029ca60c4fc017cefa54a",
|
||||
"sha256:f785af6b7cb07a9b1e5db0dea9ef9e3e8bb3d74874a0a61303eab9c16acc1999",
|
||||
"sha256:fbb645477595ce2a0fbb678d1cfd08d3b896e5d56196d40fb9e114eeab9382b3",
|
||||
"sha256:fcef31b062f756ba7eebcd7890c5d5de84b9d64ee877325257bcc9782288564a",
|
||||
"sha256:fe606e728842389943a939258809dc5db2de831b1d2e0118515059e87f7bbc1a",
|
||||
"sha256:fef4e3b3f2084b4dae3e5316b44cda72587dcc81f68b4eb2dbda1b8d15261b61",
|
||||
"sha256:ffd94b4803811c738e504a4b499fb2f848b2f7412d71e6b517508217c1d7929d"
|
||||
"sha256:00628196dd3592972a5183194ab1475dadf9ef2a4cf3fd8c7c184a94934012e8",
|
||||
"sha256:01d6dc548e7fe5c66913c2274f44855b0f8474935acff7811e84fe1f4024c94f",
|
||||
"sha256:056fe779f01a867d572e071c0944ac2f3bf58d8bced326040f0bd060af33a209",
|
||||
"sha256:080a7bf55c432abdae74f25dc3dbff407418346aeae1d43e31f65e8ef114f785",
|
||||
"sha256:0956322bf4d5e2293e57600aa929c241edf1e209e94e12483bf58c5c691432db",
|
||||
"sha256:0a6f9e897907757e9c2d722ae6c203d48a04826a14e1495e33935c8583c163a9",
|
||||
"sha256:0ac446f557eb28e3f7c65372608810ff073840627e9037e22ed10bd081793a34",
|
||||
"sha256:0b47843f2f288fa746dead4394591a3432a358aaad48240283fa230d6e74b0e7",
|
||||
"sha256:11fc8bc65f964c7278deb1b7a69379dab3ecc90095f252deb17365637ebb274d",
|
||||
"sha256:129165b68a3e0c2a633ed0d8557cf5ade24a0b37ca97d7805fa6fc5fb73c19d5",
|
||||
"sha256:139963494fc3dd5caee5e38c0a03783ef50be118565e94b1dbb0210770f0b32d",
|
||||
"sha256:157313a124287cbc8a11b55a75def0dd59e68badbc82c2dc2d204dc852742874",
|
||||
"sha256:16d0edab54bb9d214319418f65bd15cfc4210ec41a16c3dd0b71e626c803212d",
|
||||
"sha256:1971050b447023288a2b694a03b400bd5163829cd67b10f19e757fe87cd1161e",
|
||||
"sha256:1c4e75bbf9ade3d2cdf1b607a8b353b17d9b3cf54e88b2a5a773f50ae6f1bfbc",
|
||||
"sha256:1c9f36055a89b9517db66eb8e80137126bf629c767ceeade4d004e40bc8bcd99",
|
||||
"sha256:2020102a40edd094c0aa80e09203af71c533c41f76ce3237c99fd194a473ea33",
|
||||
"sha256:20febc849a1f858e6a57a7d47b323fe9e727c579ddd526d317ad8831748a66a8",
|
||||
"sha256:220d4b8649ef54ac98e5e0e3dd92230247f67270d1524a8b31aa9859007affb0",
|
||||
"sha256:22188943a29072b684cd7c99e0b2cfc0af317cea3366c583d820507e6d1f2ed4",
|
||||
"sha256:222cb27ff05bc0aec72498d075dba1facec49a76a7da45740690cebbe3e81e43",
|
||||
"sha256:243825f56b58bef28bfc602992a8c6d09bbc625628c195498d6020120d632a09",
|
||||
"sha256:25060e7162e44242a449ed1a14a4e94b5aef340812754c443459f19c7954be91",
|
||||
"sha256:26691454a6770628882b68fe74e9f84ca2a51512edd49cbb025b14df5a9dd85a",
|
||||
"sha256:27d13c7b886afc5d2fc49d6e92f9c96b1f0a14dc7b5502520c29f3da7550d401",
|
||||
"sha256:27eeee915258b105a21a4b0f8aebc5f77bb4dc4fb4063a09dd329fa1fdcbd234",
|
||||
"sha256:29ed022189a7997de46cb9bd4e2e49d6163d4f8d78dea72ac5a0e0293b856810",
|
||||
"sha256:2a324e3007afb5c667026f5235b35efe3c4a95f1b83cd93aa9fce67b42f08e7c",
|
||||
"sha256:2c533c828d0007fac27cf45e5c1a711e5914dd469db5fe6be5f4e606bf2d7f63",
|
||||
"sha256:30ba4fba3de1dca653de41c879349ec6ca521d85cff6a7ca5d2fdd8f76c93781",
|
||||
"sha256:357e07c827bad01f98d0bd0dfdc722f483febeed39140fd75ffd016a451b60b9",
|
||||
"sha256:3800f3c8c9780f281cf590543fd4b3278fea6988202273a260ecc58136895efb",
|
||||
"sha256:3d6f3a94abd8b44b2bf346ca81ab2ff41ab9146c53905eedf5178b19d9fe53bf",
|
||||
"sha256:3eb1390a8b062fe9125e5cc4c5eba990b5d383eec54f2b996e7ce73ac43150f9",
|
||||
"sha256:407920e9318d94cc6c9611aaa5b5e5963a09f1cbfa17b16b66edea453b3754f4",
|
||||
"sha256:42622c42c159ea4535bba7e1e3c97f1fec79505bc6873ae657dc0a8f861c60de",
|
||||
"sha256:4695fcd37478988b1d0a16d5bc0df56dcb677fd5db37f1893d993fd3ebef914b",
|
||||
"sha256:4798f6744fa2633666e17b4ea8ff70250781b52a25afdbf5ffb5e176c58848f1",
|
||||
"sha256:4a5b0d277087a5bf261a607fc6ff4aaffcf80b300cd19b7a1e9754a4649f5fd4",
|
||||
"sha256:4e297fd2e58afe17e33dd80c231c3a9d850279a2a8625aed1d39f9be9534809e",
|
||||
"sha256:507e567aee4806576e20752f22533e8b7ec61e7e75062a7ce9222a0675aa0da6",
|
||||
"sha256:50d702149747852923be60cae125285eca8d189d4c7d8832c0c958d4071a0f78",
|
||||
"sha256:51947a00ae9924584fb14c0c1b1f4c1fd916d9abd6f47582f318ab9c9cb9f3d0",
|
||||
"sha256:52328192d454ca2ddad09fbc088872b014c74b22ecdd5164717dc7e6442014fa",
|
||||
"sha256:531e6dfec8058fcf5d69e863b61e6b28e3749b615a4dcc0ab8ad36307c4017fc",
|
||||
"sha256:54bd71f14a5fa9bae73ef92f2e2be894dc36c7a6d1c4962e5969bd8a9aa39325",
|
||||
"sha256:552a93be286ca485914777461b384761519db313e0a7f3012dca424c9610a4d5",
|
||||
"sha256:583b46b3ba44121de5e87e95ae379932dc5fd2e37ebdf2c11a6d7975891425c1",
|
||||
"sha256:5b58a672ec448fb36839a5fc7bf2b2f60df9a97b872d8bd6ca1a28da6126f5c7",
|
||||
"sha256:5cfbdccddaa0ff07789e9e180db127906c676e479e05c04830cd458945de3511",
|
||||
"sha256:5da4939e241301f5e1d18118695e8d2c300be90431b66bd43a00376acec45e1e",
|
||||
"sha256:5dd9edcab8979a50c2c4dec6d5b66789fb6f630bb52ab90a4548111075a75e48",
|
||||
"sha256:5e304f94c0353f6ae5711533b5793b3a45b17aa2c5b07e656649b0af4e0939b5",
|
||||
"sha256:60408ec9c0bd76f1fa00d28034429a0316246d31069b982a86aec8d5c99e910a",
|
||||
"sha256:62f71b268f14ee6cc3045b95441bfe0518cef1d0b2ffbc6f3e9758f786ff5a03",
|
||||
"sha256:664d462a4c0783fd755fe3440f07b7e46d149859c96caacadf3f28890f19a8de",
|
||||
"sha256:66d8b7a89fac6042f7df9ea97d97ed0f5e404281110a882e3babd909161f85b6",
|
||||
"sha256:6755cfcfa7d8966e704d580c831e39818f85e7b2b7852ad22708973176f0009e",
|
||||
"sha256:679856547f0b27b98811b73756bdf53769c23b045a6f95177cae634daabf1ddf",
|
||||
"sha256:6841c08b51417f8ffe398b2828fc0593440c99525c868f640e0302476745320b",
|
||||
"sha256:68f6e64d4867ee79e25c49d7f35b2b1f04a6d6f778176dcf5b759f3b17a02b2b",
|
||||
"sha256:69d2d507c1174330c71c834b5d65e66181ad7b42b0d88b5b31804ee9b4f5dae7",
|
||||
"sha256:6f0be27d06732e2833b672a8fcc32fa195bdb22161eb88f8890de15e30264a01",
|
||||
"sha256:6f7d2dbe628f3db935622a5b80a5c4d95665cdefc4904372aa3c4d786289477f",
|
||||
"sha256:72760411d60d8d76979a20ed3f15586d824db04668b581b86e61158c2b616db0",
|
||||
"sha256:727f7a969416f02ef5c1256541e06f0836fb615022699fa8e2591e85296c5570",
|
||||
"sha256:77d2368a06a86a18919c05a9b4b0ee9869f770e6a5f414b0fecc911870fe3974",
|
||||
"sha256:79ab1c5f26f23e51d4a44c4397c8a3bf56c306c125dfab6b3eebdfa13d1dca6f",
|
||||
"sha256:79db23eda81627132327ed292bd813a9af64399b98aaac3d42ad8deeed24cd5e",
|
||||
"sha256:7c20d6e6cafce5027e7092beb2ac6eec0d71045d6318b34f36e1387a8c8859a3",
|
||||
"sha256:7e0851a985a7b10f634188117c825ef99d63402555cca5bc32c7bfc5adaf0d6f",
|
||||
"sha256:7e2e1ff784c2cdfd863bad31985851427f2d2796e445cec85080c7510cba4315",
|
||||
"sha256:7f8b12424f8fdf29d1c0749c628bd1530cecfc77374935d096cccc0e4eada232",
|
||||
"sha256:81e84054b22babcd6c5cc1eac0de2bfc1054ecdf742720cbfb36efbe89ec6c30",
|
||||
"sha256:84bb57010a1ab76cf880424a2e0bce8dd26989849d2122ff073aa11bfc271c27",
|
||||
"sha256:870ed23361e2918ab1ffc23fe0ab293abf3c372a68ee7387456d13da3e213008",
|
||||
"sha256:8cf44b012e7493127ce7ca6e469138ac96b3295a117877d5469aabe7c8728d87",
|
||||
"sha256:8d6c9bc14bacdfbfd51fed85f0576973eaaa7b30d81ef93264f8e22b86a9c9f7",
|
||||
"sha256:8d759cecfa8aab4a1eb4e23b6420126b15c7743e85b33f389916bb98c4ecbb84",
|
||||
"sha256:8ef3f0977c21190f949d5cfd71ded09de87d330c6d98bd5ecb5bb1135d666d0d",
|
||||
"sha256:8f95daf0ce2b24815ddf62667229ba5dfc0cfee43eb43b2549766170d0f24ae9",
|
||||
"sha256:911b4a16dce370657e5b8d8b6ba0fbb50dd5e2b24c4416f4b9e664503d3f0502",
|
||||
"sha256:96117212229905da864794df9ea7bd54987c30a5dcbab3432edc3f344231adae",
|
||||
"sha256:963cbcf296943f7017470d0b705e63e908f32b4f7dbe43f72c22f6fe1bd9ef66",
|
||||
"sha256:975a118aa019d745f1398613b27fd8789f60a8cea057a00cdc1abedee123ffe6",
|
||||
"sha256:9930853d451086c4c084d83a87294bdb0c5bc0fa4105a26c487ac09ea62e565b",
|
||||
"sha256:99d16862e802e7c50c3b6cdd1bf041b6142335c9c2b426631f731257adfe5a15",
|
||||
"sha256:9ed4a2852b3de7a64884afcc6936db771707943249a060aec8e551c16361d478",
|
||||
"sha256:9f7796959c9c036a115d34696563f75d4a2912d3b97c15c15f2a36bdd5496ce9",
|
||||
"sha256:a04b7a9017b8d0341ebbe77f61b74df1cf1b714f42b671a06f4912dc93d82597",
|
||||
"sha256:a1b3c4ca3bec8e0ad9d32ce62444c5f3913588124a922629aa7d39357b2adf3f",
|
||||
"sha256:a290a417608f50137bec731d1f22ff3efebac72845530807a8433b2db9358c95",
|
||||
"sha256:a33f7c5acf44961f29018b13f0b5f5e1589ac0cfdf75a97c9774cf7ec84d09e0",
|
||||
"sha256:a39be79a7c36e9a2e20376261c30deb3cdca86b50f7462ae9ff10a755c6720b9",
|
||||
"sha256:a50a66fa34dd7f9dcdbc7602a1b7bf6f9ab030b4f43e892324193423d9ede180",
|
||||
"sha256:a5ce1bdee102f7e60c075274df10b892d9ff5183ad6f5f515973eda8903dfe4c",
|
||||
"sha256:a763dd33d6e27c9b4db3f8089a5fa39179a8a3cf48ce702b24a857d7c621333c",
|
||||
"sha256:a773199dc42b5d02fcd46c8add552da2c4725ce2caa069527c7e27b5b6089e85",
|
||||
"sha256:aa3c925502bd0b957a96a5619134bcdc0382ef73cffd40bad218ced3586bcf8d",
|
||||
"sha256:aeb6db2f4ab54ac21a3851d05130a2aa78a6f6a5f14003f9ae3114fb8b210850",
|
||||
"sha256:af670708e145b048ead87375b899229443f2d0b4af2d1450d7701c74cd932b03",
|
||||
"sha256:afa24e5750c9b89ad5a7efef037efe49f4e339f20a94bf678c422c0c71e1207a",
|
||||
"sha256:b02cc1cac9099c0ec72da09593e7fadb1b6cf88a101acc8153592c700d732d80",
|
||||
"sha256:b37c9ea942395de029be270f0eca8c141eb14e8455941495cd3b6f95bbe465f4",
|
||||
"sha256:b3b521e117ab991d6b3b830656f464b354a42dbea2ca16a0e7d93d573f7ab7ff",
|
||||
"sha256:b5ad8261f47c2a72d0f676bc40f752db8cfdcab911e970753343836e41d5a9a7",
|
||||
"sha256:b9616ea14917d06736339cf36bb9eaf4eb52110a74136b0dc5eff94e92417d22",
|
||||
"sha256:b9a03767c937b621ee267507bc394df97fb2f8f61130f39f2033f3c6c191f124",
|
||||
"sha256:b9ae0008cff25e154ef1e3975a1705d344e844ffdeb34c25b007fd48c876e95d",
|
||||
"sha256:bdd6412c1f38da7565126b174f4e644f362e317ef0560fac1fb9d0c70900ff4d",
|
||||
"sha256:bfc417e58f277e949ed662d9cd050ddbb00c0dea8a828abaccc93dc357b7a6d1",
|
||||
"sha256:c15b9e37bbca59657e4dcc63ad068c821a4676def15f04742c406748a0a11b9c",
|
||||
"sha256:c677849947d523a082be6e0b5c9137f443a54e951a1711ef003ec52910c41ece",
|
||||
"sha256:c9d247fcc33c90f2758f4162693250341e3f38cd094f64390076ef33ad0887f9",
|
||||
"sha256:ca643295bf5441dd38dadf7571ca4b63961820eedbffbe46ceba0893bf226203",
|
||||
"sha256:ca87f639094c72268e17bc7f57c1225cc38f9e191a489a0134762e3fec402c1a",
|
||||
"sha256:cc060bc17b9de27874997d612e37d52f72092f9b59d1e04284a90ed8113cadca",
|
||||
"sha256:ccf4a73e07bfbd790443d6b3c1f1447ffda23cc9391e40c035d9b7d3514b57b8",
|
||||
"sha256:cf36cadeb9c989f760a13058dbc455e5406ec3d2d247c705c8d4bc6dd1b0fcc6",
|
||||
"sha256:d47e2bdeba4fb1986af2ba395ce51223f4d460e6e77119439e78f2b592cafade",
|
||||
"sha256:db78cc5c03b446a43413165aa873e2f408e9fd5ddb45533e7bd3b638bace867c",
|
||||
"sha256:dbc5029c61f9ebb2d4c247f13584a0ef0e8e49abb13e56460310821aca3ffcaf",
|
||||
"sha256:ddb319f869d497ef2d3d56319360b61284a9a1d8b3de3bc936748698acfda6be",
|
||||
"sha256:e0e4fdeae6c0a9d886749780ec5dcf469e98f27b312efa93008d03eaa2426fd5",
|
||||
"sha256:e4c5e7edf1e7bcbde3b52058f171a411e2a24a081b3e951d685dfea4c3c383d5",
|
||||
"sha256:e71c9dba78671d38a549e3b2d52514f50e199f9d7e18ed9b0180adeef0d04130",
|
||||
"sha256:e997d22e0d1e08c8752f61675a75d93659f7aa4dbeaee54207f8d877817b4a0c",
|
||||
"sha256:efa5834ba5e6c70b22afdca3894097e5a592d8d483c976359654ba990477799a",
|
||||
"sha256:f2d951002b11962b26afb31f758c18ad39771f287b100fa5adb1d09a47eaaf5b",
|
||||
"sha256:f3f96f57cea35ba19fd23a20b38fa0dfa3d87d582507129b8c8e314aa298f59b",
|
||||
"sha256:f738051052abc95dc17f9a4c92044294a263fb7f762efdb13e528d419005c0e4",
|
||||
"sha256:f76784355060999c36fa807b59faecb38f5769ae58283d00270835773f95e35b",
|
||||
"sha256:f92462ea3888c99439f58f7561ecd5dd4cf8b8b1b259ccf5376667b8c46ee747",
|
||||
"sha256:fefd18b29f3b84a0cdea1d86340219d9871c3b0673a38e722a73a2c39591eaa7"
|
||||
],
|
||||
"version": "==3.0.0"
|
||||
"version": "==3.6.0"
|
||||
},
|
||||
"bitstring": {
|
||||
"hashes": [
|
||||
"sha256:3282a896814813f8fe5fa09dbafac842c57aace1d3bfd94546c6f1ed9aafcbe1",
|
||||
"sha256:81800bc4e00b6508716adbae648e741256355c8dfd19541f76482fb89bee0313"
|
||||
"sha256:69d1587f0ac18dc7d93fc7e80d5f447161a33e57027e726dc18a0a8bacf1711a",
|
||||
"sha256:a08bc09d3857216d4c0f412a1611056f1cc2b64fd254fb1e8a0afba7cfa1a95a"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==4.3.0"
|
||||
"version": "==4.3.1"
|
||||
},
|
||||
"cffi": {
|
||||
"hashes": [
|
||||
@@ -247,54 +244,72 @@
|
||||
"markers": "platform_python_implementation != 'PyPy'",
|
||||
"version": "==1.17.1"
|
||||
},
|
||||
"click": {
|
||||
"hashes": [
|
||||
"sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202",
|
||||
"sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"
|
||||
],
|
||||
"markers": "python_version >= '3.10'",
|
||||
"version": "==8.2.1"
|
||||
},
|
||||
"cryptography": {
|
||||
"hashes": [
|
||||
"sha256:1923cb251c04be85eec9fda837661c67c1049063305d6be5721643c22dd4e2b7",
|
||||
"sha256:37d76e6863da3774cd9db5b409a9ecfd2c71c981c38788d3fcfaf177f447b731",
|
||||
"sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b",
|
||||
"sha256:404fdc66ee5f83a1388be54300ae978b2efd538018de18556dde92575e05defc",
|
||||
"sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543",
|
||||
"sha256:62901fb618f74d7d81bf408c8719e9ec14d863086efe4185afd07c352aee1d2c",
|
||||
"sha256:660cb7312a08bc38be15b696462fa7cc7cd85c3ed9c576e81f4dc4d8b2b31591",
|
||||
"sha256:708ee5f1bafe76d041b53a4f95eb28cdeb8d18da17e597d46d7833ee59b97ede",
|
||||
"sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb",
|
||||
"sha256:831c3c4d0774e488fdc83a1923b49b9957d33287de923d58ebd3cec47a0ae43f",
|
||||
"sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123",
|
||||
"sha256:8b3e6eae66cf54701ee7d9c83c30ac0a1e3fa17be486033000f2a73a12ab507c",
|
||||
"sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c",
|
||||
"sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285",
|
||||
"sha256:abc998e0c0eee3c8a1904221d3f67dcfa76422b23620173e28c11d3e626c21bd",
|
||||
"sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092",
|
||||
"sha256:be4ce505894d15d5c5037167ffb7f0ae90b7be6f2a98f9a5c3442395501c32fa",
|
||||
"sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289",
|
||||
"sha256:cd4e834f340b4293430701e772ec543b0fbe6c2dea510a5286fe0acabe153a02",
|
||||
"sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64",
|
||||
"sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053",
|
||||
"sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417",
|
||||
"sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e",
|
||||
"sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e",
|
||||
"sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7",
|
||||
"sha256:f5e7cb1e5e56ca0933b4873c0220a78b773b24d40d186b6738080b73d3d0a756",
|
||||
"sha256:f677e1268c4e23420c3acade68fac427fffcb8d19d7df95ed7ad17cdef8404f4"
|
||||
"sha256:00e8724bdad672d75e6f069b27970883179bd472cd24a63f6e620ca7e41cc0c5",
|
||||
"sha256:048e7ad9e08cf4c0ab07ff7f36cc3115924e22e2266e034450a890d9e312dd74",
|
||||
"sha256:0d9ef57b6768d9fa58e92f4947cea96ade1233c0e236db22ba44748ffedca394",
|
||||
"sha256:18f878a34b90d688982e43f4b700408b478102dd58b3e39de21b5ebf6509c301",
|
||||
"sha256:1b7fa6a1c1188c7ee32e47590d16a5a0646270921f8020efc9a511648e1b2e08",
|
||||
"sha256:20ae4906a13716139d6d762ceb3e0e7e110f7955f3bc3876e3a07f5daadec5f3",
|
||||
"sha256:20d15aed3ee522faac1a39fbfdfee25d17b1284bafd808e1640a74846d7c4d1b",
|
||||
"sha256:2384f2ab18d9be88a6e4f8972923405e2dbb8d3e16c6b43f15ca491d7831bd18",
|
||||
"sha256:275ba5cc0d9e320cd70f8e7b96d9e59903c815ca579ab96c1e37278d231fc402",
|
||||
"sha256:2dac5ec199038b8e131365e2324c03d20e97fe214af051d20c49db129844e8b3",
|
||||
"sha256:31a2b9a10530a1cb04ffd6aa1cd4d3be9ed49f7d77a4dafe198f3b382f41545c",
|
||||
"sha256:3436128a60a5e5490603ab2adbabc8763613f638513ffa7d311c900a8349a2a0",
|
||||
"sha256:3b5bf5267e98661b9b888a9250d05b063220dfa917a8203744454573c7eb79db",
|
||||
"sha256:3de77e4df42ac8d4e4d6cdb342d989803ad37707cf8f3fbf7b088c9cbdd46427",
|
||||
"sha256:44647c5d796f5fc042bbc6d61307d04bf29bccb74d188f18051b635f20a9c75f",
|
||||
"sha256:550ae02148206beb722cfe4ef0933f9352bab26b087af00e48fdfb9ade35c5b3",
|
||||
"sha256:599c8d7df950aa68baa7e98f7b73f4f414c9f02d0e8104a30c0182a07732638b",
|
||||
"sha256:5b64e668fc3528e77efa51ca70fadcd6610e8ab231e3e06ae2bab3b31c2b8ed9",
|
||||
"sha256:5bd6020c80c5b2b2242d6c48487d7b85700f5e0038e67b29d706f98440d66eb5",
|
||||
"sha256:5c966c732cf6e4a276ce83b6e4c729edda2df6929083a952cc7da973c539c719",
|
||||
"sha256:629127cfdcdc6806dfe234734d7cb8ac54edaf572148274fa377a7d3405b0043",
|
||||
"sha256:705bb7c7ecc3d79a50f236adda12ca331c8e7ecfbea51edd931ce5a7a7c4f012",
|
||||
"sha256:780c40fb751c7d2b0c6786ceee6b6f871e86e8718a8ff4bc35073ac353c7cd02",
|
||||
"sha256:7a3085d1b319d35296176af31c90338eeb2ddac8104661df79f80e1d9787b8b2",
|
||||
"sha256:826b46dae41a1155a0c0e66fafba43d0ede1dc16570b95e40c4d83bfcf0a451d",
|
||||
"sha256:833dc32dfc1e39b7376a87b9a6a4288a10aae234631268486558920029b086ec",
|
||||
"sha256:cc4d66f5dc4dc37b89cfef1bd5044387f7a1f6f0abb490815628501909332d5d",
|
||||
"sha256:d063341378d7ee9c91f9d23b431a3502fc8bfacd54ef0a27baa72a0843b29159",
|
||||
"sha256:e2a21a8eda2d86bb604934b6b37691585bd095c1f788530c1fcefc53a82b3453",
|
||||
"sha256:e40b80ecf35ec265c452eea0ba94c9587ca763e739b8e559c128d23bff7ebbbf",
|
||||
"sha256:e5b3dda1b00fb41da3af4c5ef3f922a200e33ee5ba0f0bc9ecf0b0c173958385",
|
||||
"sha256:ea3c42f2016a5bbf71825537c2ad753f2870191134933196bee408aac397b3d9",
|
||||
"sha256:eccddbd986e43014263eda489abbddfbc287af5cddfd690477993dbb31e31016",
|
||||
"sha256:ee411a1b977f40bd075392c80c10b58025ee5c6b47a822a33c1198598a7a5f05",
|
||||
"sha256:f4028f29a9f38a2025abedb2e409973709c660d44319c61762202206ed577c42",
|
||||
"sha256:f68f833a9d445cc49f01097d95c83a850795921b3f7cc6488731e69bde3288da",
|
||||
"sha256:fc022c1fa5acff6def2fc6d7819bbbd31ccddfe67d075331a65d9cfb28a20983"
|
||||
],
|
||||
"markers": "python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1'",
|
||||
"version": "==44.0.0"
|
||||
},
|
||||
"ecdsa": {
|
||||
"hashes": [
|
||||
"sha256:2cea9b88407fdac7bbeca0833b189e4c9c53f2ef1e1eaa29f6224dbc809b707a",
|
||||
"sha256:60eaad1199659900dd0af521ed462b793bbdf867432b3948e87416ae4caf6bf8"
|
||||
],
|
||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==0.19.0"
|
||||
"version": "==45.0.6"
|
||||
},
|
||||
"esptool": {
|
||||
"hashes": [
|
||||
"sha256:dc4ef26b659e1a8dcb019147c0ea6d94980b34de99fbe09121c7941c8b254531"
|
||||
"sha256:05cc4732eb2a9a7766c9e3531f7943d76ff0ca06dc9cd308d1d3d0b72f74aac2"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==4.8.1"
|
||||
"markers": "python_version >= '3.10'",
|
||||
"version": "==5.0.2"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9",
|
||||
"sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==3.10"
|
||||
},
|
||||
"intelhex": {
|
||||
"hashes": [
|
||||
@@ -303,14 +318,38 @@
|
||||
],
|
||||
"version": "==2.3.0"
|
||||
},
|
||||
"markdown-it-py": {
|
||||
"hashes": [
|
||||
"sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1",
|
||||
"sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==3.0.0"
|
||||
},
|
||||
"mdurl": {
|
||||
"hashes": [
|
||||
"sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8",
|
||||
"sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.1.2"
|
||||
},
|
||||
"mpremote": {
|
||||
"hashes": [
|
||||
"sha256:1a3c16d255748cfe54d4a897908651fc8286233173f7c7b2a0e56ae4b9fa940e",
|
||||
"sha256:d3ae3d0a0ae7713c537be2b6afadd11c7cde5f1750ea1260f6667bb80071b15b"
|
||||
"sha256:7f347318fb6d3bb8f89401d399a05efba39b51c74f747cebe92d3c6a9a4ee0b4",
|
||||
"sha256:daed9b795fdf98edb0c9c4f7f892bf66f075ec5e728bdcc4ab0915abf23d5d17"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_version >= '3.4'",
|
||||
"version": "==1.24.1"
|
||||
"version": "==1.26.0"
|
||||
},
|
||||
"platformdirs": {
|
||||
"hashes": [
|
||||
"sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc",
|
||||
"sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"
|
||||
],
|
||||
"markers": "python_version >= '3.9'",
|
||||
"version": "==4.3.8"
|
||||
},
|
||||
"pycparser": {
|
||||
"hashes": [
|
||||
@@ -320,6 +359,14 @@
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==2.22"
|
||||
},
|
||||
"pygments": {
|
||||
"hashes": [
|
||||
"sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887",
|
||||
"sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==2.19.2"
|
||||
},
|
||||
"pyserial": {
|
||||
"hashes": [
|
||||
"sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb",
|
||||
@@ -394,13 +441,150 @@
|
||||
],
|
||||
"version": "==1.7.0"
|
||||
},
|
||||
"six": {
|
||||
"rich": {
|
||||
"hashes": [
|
||||
"sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274",
|
||||
"sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"
|
||||
"sha256:536f5f1785986d6dbdea3c75205c473f970777b4a0d6c6dd1b696aa05a3fa04f",
|
||||
"sha256:e497a48b844b0320d45007cdebfeaeed8db2a4f4bcf49f15e455cfc4af11eaa8"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.17.0"
|
||||
"markers": "python_full_version >= '3.8.0'",
|
||||
"version": "==14.1.0"
|
||||
},
|
||||
"rich-click": {
|
||||
"hashes": [
|
||||
"sha256:c3fa81ed8a671a10de65a9e20abf642cfdac6fdb882db1ef465ee33919fbcfe2",
|
||||
"sha256:fd98c0ab9ddc1cf9c0b7463f68daf28b4d0033a74214ceb02f761b3ff2af3136"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==1.8.9"
|
||||
},
|
||||
"sniffio": {
|
||||
"hashes": [
|
||||
"sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2",
|
||||
"sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==1.3.1"
|
||||
},
|
||||
"typing-extensions": {
|
||||
"hashes": [
|
||||
"sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36",
|
||||
"sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76"
|
||||
],
|
||||
"markers": "python_version >= '3.9'",
|
||||
"version": "==4.14.1"
|
||||
},
|
||||
"watchfiles": {
|
||||
"hashes": [
|
||||
"sha256:00645eb79a3faa70d9cb15c8d4187bb72970b2470e938670240c7998dad9f13a",
|
||||
"sha256:04e4ed5d1cd3eae68c89bcc1a485a109f39f2fd8de05f705e98af6b5f1861f1f",
|
||||
"sha256:0a7d40b77f07be87c6faa93d0951a0fcd8cbca1ddff60a1b65d741bac6f3a9f6",
|
||||
"sha256:0ece16b563b17ab26eaa2d52230c9a7ae46cf01759621f4fbbca280e438267b3",
|
||||
"sha256:11ee4444250fcbeb47459a877e5e80ed994ce8e8d20283857fc128be1715dac7",
|
||||
"sha256:12b0a02a91762c08f7264e2e79542f76870c3040bbc847fb67410ab81474932a",
|
||||
"sha256:12fe8eaffaf0faa7906895b4f8bb88264035b3f0243275e0bf24af0436b27259",
|
||||
"sha256:130fc497b8ee68dce163e4254d9b0356411d1490e868bd8790028bc46c5cc297",
|
||||
"sha256:17ab167cca6339c2b830b744eaf10803d2a5b6683be4d79d8475d88b4a8a4be1",
|
||||
"sha256:199207b2d3eeaeb80ef4411875a6243d9ad8bc35b07fc42daa6b801cc39cc41c",
|
||||
"sha256:20ecc8abbd957046f1fe9562757903f5eaf57c3bce70929fda6c7711bb58074a",
|
||||
"sha256:239736577e848678e13b201bba14e89718f5c2133dfd6b1f7846fa1b58a8532b",
|
||||
"sha256:249590eb75ccc117f488e2fabd1bfa33c580e24b96f00658ad88e38844a040bb",
|
||||
"sha256:27f30e14aa1c1e91cb653f03a63445739919aef84c8d2517997a83155e7a2fcc",
|
||||
"sha256:29e7bc2eee15cbb339c68445959108803dc14ee0c7b4eea556400131a8de462b",
|
||||
"sha256:328dbc9bff7205c215a7807da7c18dce37da7da718e798356212d22696404339",
|
||||
"sha256:32d6d4e583593cb8576e129879ea0991660b935177c0f93c6681359b3654bfa9",
|
||||
"sha256:3366f56c272232860ab45c77c3ca7b74ee819c8e1f6f35a7125556b198bbc6df",
|
||||
"sha256:3434e401f3ce0ed6b42569128b3d1e3af773d7ec18751b918b89cd49c14eaafb",
|
||||
"sha256:37d3d3f7defb13f62ece99e9be912afe9dd8a0077b7c45ee5a57c74811d581a4",
|
||||
"sha256:3a6fd40bbb50d24976eb275ccb55cd1951dfb63dbc27cae3066a6ca5f4beabd5",
|
||||
"sha256:3aba215958d88182e8d2acba0fdaf687745180974946609119953c0e112397dc",
|
||||
"sha256:406520216186b99374cdb58bc48e34bb74535adec160c8459894884c983a149c",
|
||||
"sha256:4281cd9fce9fc0a9dbf0fc1217f39bf9cf2b4d315d9626ef1d4e87b84699e7e8",
|
||||
"sha256:42f92befc848bb7a19658f21f3e7bae80d7d005d13891c62c2cd4d4d0abb3433",
|
||||
"sha256:48aa25e5992b61debc908a61ab4d3f216b64f44fdaa71eb082d8b2de846b7d12",
|
||||
"sha256:5007f860c7f1f8df471e4e04aaa8c43673429047d63205d1630880f7637bca30",
|
||||
"sha256:50a51a90610d0845a5931a780d8e51d7bd7f309ebc25132ba975aca016b576a0",
|
||||
"sha256:51556d5004887045dba3acdd1fdf61dddea2be0a7e18048b5e853dcd37149b86",
|
||||
"sha256:51b81e55d40c4b4aa8658427a3ee7ea847c591ae9e8b81ef94a90b668999353c",
|
||||
"sha256:5366164391873ed76bfdf618818c82084c9db7fac82b64a20c44d335eec9ced5",
|
||||
"sha256:54062ef956807ba806559b3c3d52105ae1827a0d4ab47b621b31132b6b7e2866",
|
||||
"sha256:60022527e71d1d1fda67a33150ee42869042bce3d0fcc9cc49be009a9cded3fb",
|
||||
"sha256:622d6b2c06be19f6e89b1d951485a232e3b59618def88dbeda575ed8f0d8dbf2",
|
||||
"sha256:62cc7a30eeb0e20ecc5f4bd113cd69dcdb745a07c68c0370cea919f373f65d9e",
|
||||
"sha256:693ed7ec72cbfcee399e92c895362b6e66d63dac6b91e2c11ae03d10d503e575",
|
||||
"sha256:6d2404af8db1329f9a3c9b79ff63e0ae7131986446901582067d9304ae8aaf7f",
|
||||
"sha256:7049e52167fc75fc3cc418fc13d39a8e520cbb60ca08b47f6cedb85e181d2f2a",
|
||||
"sha256:7080c4bb3efd70a07b1cc2df99a7aa51d98685be56be6038c3169199d0a1c69f",
|
||||
"sha256:7738027989881e70e3723c75921f1efa45225084228788fc59ea8c6d732eb30d",
|
||||
"sha256:7a7bd57a1bb02f9d5c398c0c1675384e7ab1dd39da0ca50b7f09af45fa435277",
|
||||
"sha256:7b3443f4ec3ba5aa00b0e9fa90cf31d98321cbff8b925a7c7b84161619870bc9",
|
||||
"sha256:7c55b0f9f68590115c25272b06e63f0824f03d4fc7d6deed43d8ad5660cabdbf",
|
||||
"sha256:7fd1b3879a578a8ec2076c7961076df540b9af317123f84569f5a9ddee64ce92",
|
||||
"sha256:8076a5769d6bdf5f673a19d51da05fc79e2bbf25e9fe755c47595785c06a8c72",
|
||||
"sha256:80f811146831c8c86ab17b640801c25dc0a88c630e855e2bef3568f30434d52b",
|
||||
"sha256:8412eacef34cae2836d891836a7fff7b754d6bcac61f6c12ba5ca9bc7e427b68",
|
||||
"sha256:865c8e95713744cf5ae261f3067861e9da5f1370ba91fc536431e29b418676fa",
|
||||
"sha256:86b1e28d4c37e89220e924305cd9f82866bb0ace666943a6e4196c5df4d58dcc",
|
||||
"sha256:891c69e027748b4a73847335d208e374ce54ca3c335907d381fde4e41661b13b",
|
||||
"sha256:8ac164e20d17cc285f2b94dc31c384bc3aa3dd5e7490473b3db043dd70fbccfd",
|
||||
"sha256:8c5701dc474b041e2934a26d31d39f90fac8a3dee2322b39f7729867f932b1d4",
|
||||
"sha256:90ebb429e933645f3da534c89b29b665e285048973b4d2b6946526888c3eb2c7",
|
||||
"sha256:923fec6e5461c42bd7e3fd5ec37492c6f3468be0499bc0707b4bbbc16ac21792",
|
||||
"sha256:935f9edd022ec13e447e5723a7d14456c8af254544cefbc533f6dd276c9aa0d9",
|
||||
"sha256:95ab1594377effac17110e1352989bdd7bdfca9ff0e5eeccd8c69c5389b826d0",
|
||||
"sha256:9974d2f7dc561cce3bb88dfa8eb309dab64c729de85fba32e98d75cf24b66297",
|
||||
"sha256:9c733cda03b6d636b4219625a4acb5c6ffb10803338e437fb614fef9516825ef",
|
||||
"sha256:9dc001c3e10de4725c749d4c2f2bdc6ae24de5a88a339c4bce32300a31ede179",
|
||||
"sha256:9f811079d2f9795b5d48b55a37aa7773680a5659afe34b54cc1d86590a51507d",
|
||||
"sha256:a2726d7bfd9f76158c84c10a409b77a320426540df8c35be172444394b17f7ea",
|
||||
"sha256:a479466da6db5c1e8754caee6c262cd373e6e6c363172d74394f4bff3d84d7b5",
|
||||
"sha256:a543492513a93b001975ae283a51f4b67973662a375a403ae82f420d2c7205ee",
|
||||
"sha256:a89c75a5b9bc329131115a409d0acc16e8da8dfd5867ba59f1dd66ae7ea8fa82",
|
||||
"sha256:a8f6f72974a19efead54195bc9bed4d850fc047bb7aa971268fd9a8387c89011",
|
||||
"sha256:a9ccbf1f129480ed3044f540c0fdbc4ee556f7175e5ab40fe077ff6baf286d4e",
|
||||
"sha256:aa0cc8365ab29487eb4f9979fd41b22549853389e22d5de3f134a6796e1b05a4",
|
||||
"sha256:adb4167043d3a78280d5d05ce0ba22055c266cf8655ce942f2fb881262ff3cdf",
|
||||
"sha256:af06c863f152005c7592df1d6a7009c836a247c9d8adb78fef8575a5a98699db",
|
||||
"sha256:b067915e3c3936966a8607f6fe5487df0c9c4afb85226613b520890049deea20",
|
||||
"sha256:b7c5f6fe273291f4d414d55b2c80d33c457b8a42677ad14b4b47ff025d0893e4",
|
||||
"sha256:b915daeb2d8c1f5cee4b970f2e2c988ce6514aace3c9296e58dd64dc9aa5d575",
|
||||
"sha256:ba0e3255b0396cac3cc7bbace76404dd72b5438bf0d8e7cefa2f79a7f3649caa",
|
||||
"sha256:bda8136e6a80bdea23e5e74e09df0362744d24ffb8cd59c4a95a6ce3d142f79c",
|
||||
"sha256:bfe3c517c283e484843cb2e357dd57ba009cff351edf45fb455b5fbd1f45b15f",
|
||||
"sha256:c588c45da9b08ab3da81d08d7987dae6d2a3badd63acdb3e206a42dbfa7cb76f",
|
||||
"sha256:c600e85f2ffd9f1035222b1a312aff85fd11ea39baff1d705b9b047aad2ce267",
|
||||
"sha256:c68e9f1fcb4d43798ad8814c4c1b61547b014b667216cb754e606bfade587018",
|
||||
"sha256:c9649dfc57cc1f9835551deb17689e8d44666315f2e82d337b9f07bd76ae3aa2",
|
||||
"sha256:cb45350fd1dc75cd68d3d72c47f5b513cb0578da716df5fba02fff31c69d5f2d",
|
||||
"sha256:cbcf8630ef4afb05dc30107bfa17f16c0896bb30ee48fc24bf64c1f970f3b1fd",
|
||||
"sha256:cbd949bdd87567b0ad183d7676feb98136cde5bb9025403794a4c0db28ed3a47",
|
||||
"sha256:cc08ef8b90d78bfac66f0def80240b0197008e4852c9f285907377b2947ffdcb",
|
||||
"sha256:cd17a1e489f02ce9117b0de3c0b1fab1c3e2eedc82311b299ee6b6faf6c23a29",
|
||||
"sha256:d05686b5487cfa2e2c28ff1aa370ea3e6c5accfe6435944ddea1e10d93872147",
|
||||
"sha256:d0e10e6f8f6dc5762adee7dece33b722282e1f59aa6a55da5d493a97282fedd8",
|
||||
"sha256:d181ef50923c29cf0450c3cd47e2f0557b62218c50b2ab8ce2ecaa02bd97e670",
|
||||
"sha256:d1caf40c1c657b27858f9774d5c0e232089bca9cb8ee17ce7478c6e9264d2587",
|
||||
"sha256:d7642b9bc4827b5518ebdb3b82698ada8c14c7661ddec5fe719f3e56ccd13c97",
|
||||
"sha256:d9481174d3ed982e269c090f780122fb59cee6c3796f74efe74e70f7780ed94c",
|
||||
"sha256:d9ba68ec283153dead62cbe81872d28e053745f12335d037de9cbd14bd1877f5",
|
||||
"sha256:da71945c9ace018d8634822f16cbc2a78323ef6c876b1d34bbf5d5222fd6a72e",
|
||||
"sha256:dc44678a72ac0910bac46fa6a0de6af9ba1355669b3dfaf1ce5f05ca7a74364e",
|
||||
"sha256:df32d59cb9780f66d165a9a7a26f19df2c7d24e3bd58713108b41d0ff4f929c6",
|
||||
"sha256:df670918eb7dd719642e05979fc84704af913d563fd17ed636f7c4783003fdcc",
|
||||
"sha256:e78b6ed8165996013165eeabd875c5dfc19d41b54f94b40e9fff0eb3193e5e8e",
|
||||
"sha256:ed8fc66786de8d0376f9f913c09e963c66e90ced9aa11997f93bdb30f7c872a8",
|
||||
"sha256:eff4b8d89f444f7e49136dc695599a591ff769300734446c0a86cba2eb2f9895",
|
||||
"sha256:f21af781a4a6fbad54f03c598ab620e3a77032c5878f3d780448421a6e1818c7",
|
||||
"sha256:f2bcdc54ea267fe72bfc7d83c041e4eb58d7d8dc6f578dfddb52f037ce62f432",
|
||||
"sha256:f2f0498b7d2a3c072766dba3274fe22a183dbea1f99d188f1c6c72209a1063dc",
|
||||
"sha256:f7208ab6e009c627b7557ce55c465c98967e8caa8b11833531fdf95799372633",
|
||||
"sha256:f7590d5a455321e53857892ab8879dce62d1f4b04748769f5adf2e707afb9d4f",
|
||||
"sha256:fa257a4d0d21fcbca5b5fcba9dca5a78011cb93c0323fb8855c6d2dfbc76eb77",
|
||||
"sha256:fba9b62da882c1be1280a7584ec4515d0a6006a94d6e5819730ec2eab60ffe12",
|
||||
"sha256:fe4371595edf78c41ef8ac8df20df3943e13defd0efcb732b2e393b5a8a7a71f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_version >= '3.9'",
|
||||
"version": "==1.1.0"
|
||||
}
|
||||
},
|
||||
"develop": {}
|
||||
|
12
README.md
12
README.md
@@ -23,8 +23,16 @@ pipenv run esptool.py --port PORTNAME --baud 460800 write_flash 0 ESP32_GENERIC_
|
||||
|
||||
## Upload src, reset and follow
|
||||
|
||||
```pipenv run ./dev.py <PORT> src reset follow```
|
||||
|
||||
```pipenv run ./dev.py PORTNAME src reset follow```
|
||||
|
||||
|
||||
## Connect
|
||||
|
||||
Connect to wifi nextwork called led-xxxxxx...
|
||||
|
||||
http://192.168.4.1/
|
||||
|
||||
|
||||
|
||||

|
||||

|
BIN
screenshots/controls.png
Normal file
BIN
screenshots/controls.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
BIN
screenshots/settings.png
Normal file
BIN
screenshots/settings.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
20
src/boot.py
20
src/boot.py
@@ -1,19 +1,9 @@
|
||||
import settings
|
||||
import wifi
|
||||
import time
|
||||
from settings import Settings
|
||||
|
||||
print(wifi.ap('qwerty'))
|
||||
s = Settings()
|
||||
|
||||
|
||||
settings = Settings()
|
||||
ssid = settings.get('wifi', {}).get('ssid', None)
|
||||
password = settings.get('wifi', {}).get('password', None)
|
||||
ip = settings.get('wifi', {}).get('ip', None)
|
||||
gateway = settings.get('wifi', {}).get('gateway', None)
|
||||
|
||||
for i in range(10):
|
||||
config = wifi.connect(ssid, password, ip, gateway)
|
||||
if config:
|
||||
print(config)
|
||||
break
|
||||
time.sleep(0.1)
|
||||
name = s.get('name', 'led')
|
||||
password = s.get("ap_password", "")
|
||||
wifi.ap(name, password)
|
||||
|
45
src/main.py
45
src/main.py
@@ -1,27 +1,39 @@
|
||||
import asyncio
|
||||
import aioespnow
|
||||
from settings import Settings
|
||||
from web import web
|
||||
from patterns import Patterns
|
||||
import gc
|
||||
import utime
|
||||
import machine
|
||||
import ntptime
|
||||
import time
|
||||
import wifi
|
||||
|
||||
import json
|
||||
from p2p import p2p
|
||||
|
||||
async def main():
|
||||
|
||||
|
||||
|
||||
settings = Settings()
|
||||
|
||||
patterns = Patterns(4, settings["num_leds"], selected=settings["selected_pattern"])
|
||||
patterns.set_color1(tuple(int(settings["color1"][i:i+2], 16) for i in (1, 5, 3)))
|
||||
patterns.set_color2(tuple(int(settings["color2"][i:i+2], 16) for i in (1, 5, 3)))
|
||||
patterns = Patterns(settings["led_pin"], settings["num_leds"], selected=settings["pattern"])
|
||||
if settings["color_order"] == "rbg": color_order = (1, 5, 3)
|
||||
else: color_order = (1, 3, 5)
|
||||
patterns.set_color1(tuple(int(settings["color1"][i:i+2], 16) for i in color_order))
|
||||
patterns.set_color2(tuple(int(settings["color2"][i:i+2], 16) for i in color_order))
|
||||
patterns.set_brightness(int(settings["brightness"]))
|
||||
patterns.set_delay(int(settings["delay"]))
|
||||
|
||||
async def tick():
|
||||
while True:
|
||||
patterns.tick()
|
||||
await asyncio.sleep_ms(0)
|
||||
|
||||
async def system():
|
||||
while True:
|
||||
gc.collect()
|
||||
for i in range(60):
|
||||
wdt.feed()
|
||||
await asyncio.sleep(1)
|
||||
|
||||
w = web(settings, patterns)
|
||||
print(settings)
|
||||
# start the server in a bacakground task
|
||||
@@ -30,26 +42,13 @@ async def main():
|
||||
wdt = machine.WDT(timeout=10000)
|
||||
wdt.feed()
|
||||
|
||||
async def tick():
|
||||
while True:
|
||||
patterns.tick()
|
||||
await asyncio.sleep_ms(1)
|
||||
|
||||
asyncio.create_task(tick())
|
||||
asyncio.create_task(p2p(settings, patterns))
|
||||
asyncio.create_task(system())
|
||||
|
||||
first = True
|
||||
|
||||
while True:
|
||||
#print(time.localtime())
|
||||
|
||||
# gc.collect()
|
||||
for i in range(60):
|
||||
wdt.feed()
|
||||
await asyncio.sleep_ms(500)
|
||||
|
||||
# cleanup before ending the application
|
||||
await server
|
||||
|
||||
asyncio.run(main())
|
||||
|
||||
|
||||
|
20
src/p2p.py
Normal file
20
src/p2p.py
Normal file
@@ -0,0 +1,20 @@
|
||||
import asyncio
|
||||
import aioespnow
|
||||
import json
|
||||
|
||||
async def p2p(settings, patterns):
|
||||
e = aioespnow.AIOESPNow() # Returns AIOESPNow enhanced with async support
|
||||
e.active(True)
|
||||
async for mac, msg in e:
|
||||
try:
|
||||
data = json.loads(msg)
|
||||
except:
|
||||
print(f"Failed to load espnow data {msg}")
|
||||
continue
|
||||
print(data)
|
||||
if "names" not in data or settings.get("name") in data.get("names", []):
|
||||
if "step" in settings and isinstance(settings["step"], int):
|
||||
patterns.set_pattern_step(settings["step"])
|
||||
else:
|
||||
settings.set_settings(data.get("settings", {}), patterns, data.get("save", False))
|
||||
print("should not print")
|
447
src/patterns.py
447
src/patterns.py
@@ -18,27 +18,49 @@ class Patterns:
|
||||
"rainbow_cycle": self.rainbow_cycle_step,
|
||||
"theater_chase": self.theater_chase_step,
|
||||
"blink": self.blink_step,
|
||||
"random_color_wipe": self.random_color_wipe_step,
|
||||
"random_rainbow_cycle": self.random_rainbow_cycle_step,
|
||||
"random_theater_chase": self.random_theater_chase_step,
|
||||
"random_blink": self.random_blink_step,
|
||||
"color_transition": self.color_transition_step,
|
||||
"color_transition": self.color_transition_step, # Added new pattern
|
||||
"flicker": self.flicker_step,
|
||||
"scanner": self.scanner_step, # New: Single direction scanner
|
||||
"bidirectional_scanner": self.bidirectional_scanner_step, # New: Bidirectional scanner
|
||||
"external": None
|
||||
}
|
||||
self.selected = selected
|
||||
self.color1 = color1
|
||||
self.color2 = color2
|
||||
self.transition_duration = 50 # Duration of color transition in milliseconds
|
||||
self.transition_step = 0
|
||||
|
||||
# Ensure colors list always starts with at least two for robust transition handling
|
||||
self.colors = [color1, color2] if color1 != color2 else [color1, (255, 255, 255)] # Fallback if initial colors are same
|
||||
if not self.colors: # Ensure at least one color exists
|
||||
self.colors = [(0, 0, 0)]
|
||||
|
||||
self.transition_duration = delay * 50 # Default transition duration
|
||||
self.hold_duration = delay * 10 # Default hold duration at each color
|
||||
self.transition_step = 0 # Current step in the transition
|
||||
self.current_color_idx = 0 # Index of the color currently being held/transitioned from
|
||||
self.current_color = self.colors[self.current_color_idx] # The actual blended color
|
||||
|
||||
self.hold_start_time = utime.ticks_ms() # Time when the current color hold started
|
||||
|
||||
# New attributes for scanner patterns
|
||||
self.scanner_direction = 1 # 1 for forward, -1 for backward
|
||||
self.scanner_tail_length = 3 # Number of trailing pixels
|
||||
|
||||
def sync(self):
|
||||
self.pattern_step=0
|
||||
self.last_update = utime.ticks_ms()
|
||||
self.last_update = utime.ticks_ms() - self.delay
|
||||
if self.selected == "color_transition":
|
||||
self.transition_step = 0
|
||||
self.current_color_idx = 0
|
||||
self.current_color = self.colors[self.current_color_idx]
|
||||
self.hold_start_time = utime.ticks_ms() # Reset hold time
|
||||
# Reset scanner specific variables
|
||||
self.scanner_direction = 1
|
||||
self.tick()
|
||||
|
||||
def set_pattern_step(self, step):
|
||||
self.pattern_step = step
|
||||
|
||||
def tick(self):
|
||||
if self.patterns[self.selected]:
|
||||
self.patterns[self.selected]()
|
||||
|
||||
|
||||
def update_num_leds(self, pin, num_leds):
|
||||
self.n = NeoPixel(Pin(pin, Pin.OUT), num_leds)
|
||||
self.num_leds = num_leds
|
||||
@@ -46,52 +68,145 @@ class Patterns:
|
||||
|
||||
def set_delay(self, delay):
|
||||
self.delay = delay
|
||||
# Update transition duration and hold duration when delay changes
|
||||
self.transition_duration = self.delay * 50
|
||||
self.hold_duration = self.delay * 10
|
||||
|
||||
|
||||
def set_brightness(self, brightness):
|
||||
self.brightness = brightness
|
||||
|
||||
|
||||
def set_color1(self, color):
|
||||
print(color)
|
||||
self.color1 = self.apply_brightness(color)
|
||||
|
||||
if len(self.colors) > 0:
|
||||
self.colors[0] = color
|
||||
if self.selected == "color_transition":
|
||||
# If the first color is changed, potentially reset transition
|
||||
# to start from this new color if we were about to transition from it
|
||||
if self.current_color_idx == 0:
|
||||
self.transition_step = 0
|
||||
self.current_color = self.colors[0]
|
||||
self.hold_start_time = utime.ticks_ms()
|
||||
else:
|
||||
self.colors.append(color)
|
||||
|
||||
|
||||
def set_color2(self, color):
|
||||
self.color2 = self.apply_brightness(color)
|
||||
|
||||
def apply_brightness(self, color):
|
||||
return tuple(int(c * self.brightness / 255) for c in color)
|
||||
|
||||
if len(self.colors) > 1:
|
||||
self.colors[1] = color
|
||||
elif len(self.colors) == 1:
|
||||
self.colors.append(color)
|
||||
else: # List is empty
|
||||
self.colors.append((0,0,0)) # Dummy color
|
||||
self.colors.append(color)
|
||||
|
||||
|
||||
def set_colors(self, colors):
|
||||
if colors and len(colors) >= 2:
|
||||
self.colors = colors
|
||||
if self.selected == "color_transition":
|
||||
self.sync() # Reset transition if new color list is provided
|
||||
elif colors and len(colors) == 1:
|
||||
self.colors = [colors[0], (255,255,255)] # Add a default second color
|
||||
if self.selected == "color_transition":
|
||||
print("Warning: 'color_transition' requires at least two colors. Adding a default second color.")
|
||||
self.sync()
|
||||
else:
|
||||
print("Error: set_colors requires a list of at least one color.")
|
||||
self.colors = [(0,0,0), (255,255,255)] # Fallback
|
||||
if self.selected == "color_transition":
|
||||
self.sync()
|
||||
|
||||
def set_color(self, num, color):
|
||||
# Changed: More robust index check
|
||||
if 0 <= num < len(self.colors):
|
||||
self.colors[num] = color
|
||||
# If the changed color is part of the current or next transition,
|
||||
# restart the transition for smoother updates
|
||||
if self.selected == "color_transition":
|
||||
current_from_idx = self.current_color_idx
|
||||
current_to_idx = (self.current_color_idx + 1) % len(self.colors)
|
||||
if num == current_from_idx or num == current_to_idx:
|
||||
# If we change a color involved in the current transition,
|
||||
# it's best to restart the transition state for smoothness.
|
||||
self.transition_step = 0
|
||||
self.current_color_idx = current_from_idx # Stay at the current starting color
|
||||
self.current_color = self.colors[self.current_color_idx]
|
||||
self.hold_start_time = utime.ticks_ms() # Reset hold
|
||||
return True
|
||||
elif num == len(self.colors): # Allow setting a new color at the end
|
||||
self.colors.append(color)
|
||||
return True
|
||||
return False
|
||||
|
||||
def add_color(self, color):
|
||||
self.colors.append(color)
|
||||
if self.selected == "color_transition" and len(self.colors) == 2:
|
||||
# If we just added the second color needed for transition
|
||||
self.sync()
|
||||
|
||||
|
||||
def del_color(self, num):
|
||||
# Changed: More robust index check and using del for lists
|
||||
if 0 <= num < len(self.colors):
|
||||
del self.colors[num]
|
||||
# If the color being deleted was part of the current transition,
|
||||
# re-evaluate the current_color_idx
|
||||
if self.selected == "color_transition":
|
||||
if len(self.colors) < 2: # Need at least two colors for transition
|
||||
print("Warning: Not enough colors for 'color_transition'. Switching to 'on'.")
|
||||
self.select("on") # Or some other default
|
||||
else:
|
||||
# Adjust index if it's out of bounds after deletion or was the one transitioning from
|
||||
self.current_color_idx %= len(self.colors)
|
||||
self.transition_step = 0
|
||||
self.current_color = self.colors[self.current_color_idx]
|
||||
self.hold_start_time = utime.ticks_ms()
|
||||
return True
|
||||
return False
|
||||
|
||||
def apply_brightness(self, color, brightness_override=None):
|
||||
effective_brightness = brightness_override if brightness_override is not None else self.brightness
|
||||
return tuple(int(c * effective_brightness / 255) for c in color)
|
||||
|
||||
def select(self, pattern):
|
||||
if pattern in self.patterns:
|
||||
self.selected = pattern
|
||||
self.sync() # Reset pattern state when selecting a new pattern
|
||||
if pattern == "color_transition":
|
||||
if len(self.colors) < 2:
|
||||
print("Warning: 'color_transition' requires at least two colors. Switching to 'on'.")
|
||||
self.selected = "on" # Fallback if not enough colors
|
||||
self.sync() # Re-sync for the new pattern
|
||||
else:
|
||||
self.transition_step = 0
|
||||
self.current_color_idx = 0 # Start from the first color in the list
|
||||
self.current_color = self.colors[self.current_color_idx]
|
||||
self.hold_start_time = utime.ticks_ms() # Reset hold timer
|
||||
self.transition_duration = self.delay * 50 # Initialize transition duration
|
||||
self.hold_duration = self.delay * 10 # Initialize hold duration
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def set(self, i, color):
|
||||
self.n[i] = color
|
||||
|
||||
def write(self):
|
||||
self.n.write()
|
||||
|
||||
def fill(self):
|
||||
|
||||
def fill(self, color=None):
|
||||
fill_color = color if color is not None else self.colors[0]
|
||||
for i in range(self.num_leds):
|
||||
self.n[i] = self.color1
|
||||
self.n[i] = fill_color
|
||||
self.n.write()
|
||||
|
||||
def off(self):
|
||||
color = self.color1
|
||||
self.color1 = (0,0,0)
|
||||
self.fill()
|
||||
self.color1 = color
|
||||
|
||||
self.fill((0, 0, 0))
|
||||
|
||||
def on(self):
|
||||
color = self.color1
|
||||
self.color1 = self.apply_brightness(self.color1)
|
||||
self.fill()
|
||||
self.color1 = color
|
||||
|
||||
|
||||
self.fill(self.apply_brightness(self.colors[0]))
|
||||
|
||||
def color_wipe_step(self):
|
||||
color = self.apply_brightness(self.color1)
|
||||
color = self.apply_brightness(self.colors[0])
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay:
|
||||
if self.pattern_step < self.num_leds:
|
||||
@@ -129,7 +244,7 @@ class Patterns:
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay:
|
||||
for i in range(self.num_leds):
|
||||
if (i + self.pattern_step) % 3 == 0:
|
||||
self.n[i] = self.apply_brightness(self.color1)
|
||||
self.n[i] = self.apply_brightness(self.colors[0])
|
||||
else:
|
||||
self.n[i] = (0, 0, 0)
|
||||
self.n.write()
|
||||
@@ -140,152 +255,142 @@ class Patterns:
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay:
|
||||
if self.pattern_step % 2 == 0:
|
||||
for i in range(self.num_leds):
|
||||
self.n[i] = self.apply_brightness(self.color1)
|
||||
self.fill(self.apply_brightness(self.colors[0]))
|
||||
else:
|
||||
for i in range(self.num_leds):
|
||||
self.n[i] = (0, 0, 0)
|
||||
self.n.write()
|
||||
self.pattern_step = (self.pattern_step + 1) % 2
|
||||
self.last_update = current_time
|
||||
|
||||
def random_color_wipe_step(self):
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay:
|
||||
color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
|
||||
if self.pattern_step < self.num_leds:
|
||||
for i in range(self.num_leds):
|
||||
self.n[i] = (0, 0, 0)
|
||||
self.n[self.pattern_step] = self.apply_brightness(color)
|
||||
self.n.write()
|
||||
self.pattern_step += 1
|
||||
else:
|
||||
self.pattern_step = 0
|
||||
self.last_update = current_time
|
||||
|
||||
def random_rainbow_cycle_step(self):
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay:
|
||||
def wheel(pos):
|
||||
if pos < 85:
|
||||
return (pos * 3, 255 - pos * 3, 0)
|
||||
elif pos < 170:
|
||||
pos -= 85
|
||||
return (255 - pos * 3, 0, pos * 3)
|
||||
else:
|
||||
pos -= 170
|
||||
return (0, pos * 3, 255 - pos * 3)
|
||||
|
||||
random_offset = random.randint(0, 255)
|
||||
for i in range(self.num_leds):
|
||||
rc_index = (i * 256 // self.num_leds) + self.pattern_step + random_offset
|
||||
self.n[i] = self.apply_brightness(wheel(rc_index & 255))
|
||||
self.n.write()
|
||||
self.pattern_step = (self.pattern_step + 1) % 256
|
||||
self.last_update = current_time
|
||||
|
||||
def random_theater_chase_step(self):
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay:
|
||||
color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
|
||||
for i in range(self.num_leds):
|
||||
if (i + self.pattern_step) % 3 == 0:
|
||||
self.n[i] = self.apply_brightness(color)
|
||||
else:
|
||||
self.n[i] = (0, 0, 0)
|
||||
self.n.write()
|
||||
self.pattern_step = (self.pattern_step + 1) % 3
|
||||
self.last_update = current_time
|
||||
|
||||
def random_blink_step(self):
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay:
|
||||
color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
|
||||
if self.pattern_step % 2 == 0:
|
||||
for i in range(self.num_leds):
|
||||
self.n[i] = self.apply_brightness(color)
|
||||
else:
|
||||
for i in range(self.num_leds):
|
||||
self.n[i] = (0, 0, 0)
|
||||
self.n.write()
|
||||
self.fill((0, 0, 0))
|
||||
self.pattern_step = (self.pattern_step + 1) % 2
|
||||
self.last_update = current_time
|
||||
|
||||
def color_transition_step(self):
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay:
|
||||
# Calculate transition factor based on elapsed time
|
||||
transition_factor = (self.pattern_step * 100) / self.transition_duration
|
||||
if transition_factor > 100:
|
||||
transition_factor = 100
|
||||
color = self.interpolate_color(self.color1, self.color2, transition_factor / 100)
|
||||
|
||||
# Apply the interpolated color to all LEDs
|
||||
for i in range(self.num_leds):
|
||||
self.n[i] = self.apply_brightness(color)
|
||||
self.n.write()
|
||||
|
||||
self.pattern_step += self.delay
|
||||
if self.pattern_step > self.transition_duration:
|
||||
self.pattern_step = 0
|
||||
# Check for hold duration first
|
||||
if utime.ticks_diff(current_time, self.hold_start_time) < self.hold_duration:
|
||||
# Still in hold phase, just display the current solid color
|
||||
self.fill(self.apply_brightness(self.current_color))
|
||||
self.last_update = current_time # Keep updating last_update to avoid skipping frames
|
||||
return
|
||||
|
||||
# If hold duration is over, proceed with transition
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay:
|
||||
num_colors = len(self.colors)
|
||||
if num_colors < 2:
|
||||
# Should not happen if select handles it, but as a safeguard
|
||||
self.select("on")
|
||||
return
|
||||
|
||||
from_color = self.colors[self.current_color_idx]
|
||||
to_color_idx = (self.current_color_idx + 1) % num_colors
|
||||
to_color = self.colors[to_color_idx]
|
||||
|
||||
# Calculate interpolation factor (0.0 to 1.0)
|
||||
# transition_step goes from 0 to transition_duration - 1
|
||||
if self.transition_duration > 0:
|
||||
interp_factor = self.transition_step / self.transition_duration
|
||||
else:
|
||||
interp_factor = 1.0 # Immediately transition if duration is zero
|
||||
|
||||
# Interpolate each color component
|
||||
r = int(from_color[0] + (to_color[0] - from_color[0]) * interp_factor)
|
||||
g = int(from_color[1] + (to_color[1] - from_color[1]) * interp_factor)
|
||||
b = int(from_color[2] + (to_color[2] - from_color[2]) * interp_factor)
|
||||
|
||||
self.current_color = (r, g, b)
|
||||
self.fill(self.apply_brightness(self.current_color))
|
||||
|
||||
self.transition_step += self.delay # Advance the transition step by the delay
|
||||
|
||||
if self.transition_step >= self.transition_duration:
|
||||
# Transition complete, move to the next color and reset for hold phase
|
||||
self.current_color_idx = to_color_idx
|
||||
self.current_color = self.colors[self.current_color_idx] # Ensure current_color is the exact target color
|
||||
self.transition_step = 0 # Reset transition progress
|
||||
self.hold_start_time = current_time # Start hold phase for the new color
|
||||
|
||||
self.last_update = current_time
|
||||
|
||||
def interpolate_color(self, color1, color2, factor):
|
||||
return (
|
||||
int(color1[0] + (color2[0] - color1[0]) * factor),
|
||||
int(color1[1] + (color2[1] - color1[1]) * factor),
|
||||
int(color1[2] + (color2[2] - color1[2]) * factor)
|
||||
)
|
||||
|
||||
def two_steps_forward_one_step_back_step(self):
|
||||
def flicker_step(self):
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay/5:
|
||||
base_color = self.colors[0]
|
||||
# Increase the range for flicker_brightness_offset
|
||||
# Changed from self.brightness // 4 to self.brightness // 2 (or even self.brightness for max intensity)
|
||||
flicker_brightness_offset = random.randint(-int(self.brightness // 1.5), int(self.brightness // 1.5))
|
||||
flicker_brightness = max(0, min(255, self.brightness + flicker_brightness_offset))
|
||||
|
||||
flicker_color = self.apply_brightness(base_color, brightness_override=flicker_brightness)
|
||||
self.fill(flicker_color)
|
||||
self.last_update = current_time
|
||||
|
||||
def scanner_step(self):
|
||||
"""
|
||||
Mimics a 'Knight Rider' style scanner, moving in one direction.
|
||||
"""
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay:
|
||||
# Move forward 2 steps and backward 1 step
|
||||
if self.direction == 1: # Moving forward
|
||||
if self.scanner_position < self.num_leds - 2:
|
||||
self.scanner_position += 2 # Move forward 2 steps
|
||||
else:
|
||||
self.direction = -1 # Change direction to backward
|
||||
else: # Moving backward
|
||||
if self.scanner_position > 0:
|
||||
self.scanner_position -= 1 # Move backward 1 step
|
||||
else:
|
||||
self.direction = 1 # Change direction to forward
|
||||
|
||||
# Set all LEDs to off
|
||||
for i in range(self.num_leds):
|
||||
self.n[i] = (0, 0, 0)
|
||||
|
||||
# Set the current position to the color
|
||||
self.n[self.scanner_position] = self.apply_brightness(self.color1)
|
||||
|
||||
# Apply the color transition
|
||||
transition_factor = (self.pattern_step * 100) / self.transition_duration
|
||||
if transition_factor > 100:
|
||||
transition_factor = 100
|
||||
color = self.interpolate_color(self.color1, self.color2, transition_factor / 100)
|
||||
self.n[self.scanner_position] = self.apply_brightness(color)
|
||||
|
||||
self.fill((0, 0, 0)) # Clear all LEDs
|
||||
|
||||
# Calculate the head and tail position
|
||||
head_pos = self.pattern_step
|
||||
color = self.apply_brightness(self.colors[0])
|
||||
|
||||
# Draw the head
|
||||
if 0 <= head_pos < self.num_leds:
|
||||
self.n[head_pos] = color
|
||||
|
||||
# Draw the trailing pixels with decreasing brightness
|
||||
for i in range(1, self.scanner_tail_length + 1):
|
||||
tail_pos = head_pos - i
|
||||
if 0 <= tail_pos < self.num_leds:
|
||||
# Calculate fading color for tail
|
||||
# Example: linear fade from full brightness to off
|
||||
fade_factor = 1.0 - (i / (self.scanner_tail_length + 1))
|
||||
faded_color = tuple(int(c * fade_factor) for c in color)
|
||||
self.n[tail_pos] = faded_color
|
||||
|
||||
self.n.write()
|
||||
self.pattern_step += self.delay
|
||||
if self.pattern_step > self.transition_duration:
|
||||
self.pattern_step = 0
|
||||
|
||||
self.pattern_step += 1
|
||||
if self.pattern_step >= self.num_leds + self.scanner_tail_length:
|
||||
self.pattern_step = 0 # Reset to start
|
||||
|
||||
self.last_update = current_time
|
||||
|
||||
if __name__ == "__main__":
|
||||
p = Patterns(4, 180)
|
||||
p.set_color1((255,0,0))
|
||||
p.set_color2((0,255,0))
|
||||
#p.set_delay(10)
|
||||
try:
|
||||
while True:
|
||||
for key in p.patterns:
|
||||
print(key)
|
||||
p.select(key)
|
||||
for _ in range(2000):
|
||||
p.tick()
|
||||
utime.sleep_ms(1)
|
||||
except KeyboardInterrupt:
|
||||
p.fill((0, 0, 0))
|
||||
def bidirectional_scanner_step(self):
|
||||
"""
|
||||
Mimics a 'Knight Rider' style scanner, moving back and forth.
|
||||
"""
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay/100:
|
||||
self.fill((0, 0, 0)) # Clear all LEDs
|
||||
|
||||
color = self.apply_brightness(self.colors[0])
|
||||
|
||||
# Calculate the head position based on direction
|
||||
head_pos = self.pattern_step
|
||||
|
||||
# Draw the head
|
||||
if 0 <= head_pos < self.num_leds:
|
||||
self.n[head_pos] = color
|
||||
|
||||
# Draw the trailing pixels with decreasing brightness
|
||||
for i in range(1, self.scanner_tail_length + 1):
|
||||
tail_pos = head_pos - (i * self.scanner_direction)
|
||||
if 0 <= tail_pos < self.num_leds:
|
||||
fade_factor = 1.0 - (i / (self.scanner_tail_length + 1))
|
||||
faded_color = tuple(int(c * fade_factor) for c in color)
|
||||
self.n[tail_pos] = faded_color
|
||||
|
||||
self.n.write()
|
||||
|
||||
self.pattern_step += self.scanner_direction
|
||||
|
||||
# Change direction if boundaries are reached
|
||||
if self.scanner_direction == 1 and self.pattern_step >= self.num_leds:
|
||||
self.scanner_direction = -1
|
||||
self.pattern_step = self.num_leds - 1 # Start moving back from the last LED
|
||||
elif self.scanner_direction == -1 and self.pattern_step < 0:
|
||||
self.scanner_direction = 1
|
||||
self.pattern_step = 0 # Start moving forward from the first LED
|
||||
|
||||
self.last_update = current_time
|
||||
|
@@ -1,4 +1,7 @@
|
||||
import json
|
||||
import wifi
|
||||
import ubinascii
|
||||
import machine
|
||||
|
||||
class Settings(dict):
|
||||
SETTINGS_FILE = "/settings.json"
|
||||
@@ -6,15 +9,21 @@ class Settings(dict):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.load() # Load settings from file during initialization
|
||||
if self["color_order"] == "rbg": self.color_order = (1, 5, 3)
|
||||
else: self.color_order = (1, 3, 5)
|
||||
|
||||
def set_defaults(self):
|
||||
self["led_pin"] = 10
|
||||
self["num_leds"] = 50
|
||||
self["selected_pattern"] = "blink"
|
||||
self["color1"] = "#000f00"
|
||||
self["color2"] = "#0f0000"
|
||||
self["pattern"] = "on"
|
||||
self["color1"] = "#00ff00"
|
||||
self["color2"] = "#ff0000"
|
||||
self["delay"] = 100
|
||||
self["brightness"] = 100
|
||||
self["wifi"] = {"ssid": "", "password": ""}
|
||||
self["brightness"] = 10
|
||||
self["color_order"] = "rgb"
|
||||
self["name"] = f"led-{ubinascii.hexlify(wifi.get_mac()).decode()}"
|
||||
self["ap_password"] = ""
|
||||
self["id"] = 0
|
||||
|
||||
def save(self):
|
||||
try:
|
||||
@@ -34,6 +43,55 @@ class Settings(dict):
|
||||
except Exception as e:
|
||||
print(f"Error loading settings")
|
||||
self.set_defaults()
|
||||
self.save()
|
||||
|
||||
def set_settings(self, data, patterns, save):
|
||||
try:
|
||||
print(data)
|
||||
for key, value in data.items():
|
||||
print(key, value)
|
||||
if key == "colors":
|
||||
buff = []
|
||||
for color in value:
|
||||
buff.append(tuple(int(color[i:i+2], 16) for i in self.color_order))
|
||||
patterns.set_colors(buff)
|
||||
elif key == "color1":
|
||||
patterns.set_color1(tuple(int(value[i:i+2], 16) for i in self.color_order)) # Convert hex to RGB
|
||||
elif key == "color2":
|
||||
patterns.set_color2(tuple(int(value[i:i+2], 16) for i in self.color_order)) # Convert hex to RGB
|
||||
elif key == "num_leds":
|
||||
patterns.update_num_leds(self["led_pin"], value)
|
||||
elif key == "pattern":
|
||||
if not patterns.select(value):
|
||||
return "Pattern doesn't exist", 400
|
||||
elif key == "delay":
|
||||
delay = int(data["delay"])
|
||||
patterns.set_delay(delay)
|
||||
elif key == "brightness":
|
||||
brightness = int(data["brightness"])
|
||||
patterns.set_brightness(brightness)
|
||||
elif key == "name":
|
||||
self[key] = value
|
||||
self.save()
|
||||
machine.reset()
|
||||
elif key == "color_order":
|
||||
if value == "rbg": self.color_order = (1, 5, 3)
|
||||
else: self.color_order = (1, 3, 5)
|
||||
pass
|
||||
elif key == "id":
|
||||
pass
|
||||
elif key == "led_pin":
|
||||
patterns.update_num_leds(value, self["num_leds"])
|
||||
else:
|
||||
return "Invalid key", 400
|
||||
self[key] = value
|
||||
#print(self)
|
||||
patterns.sync()
|
||||
if save:
|
||||
self.save()
|
||||
return "OK", 200
|
||||
except (KeyError, ValueError):
|
||||
return "Bad request", 400
|
||||
|
||||
# Example usage
|
||||
def main():
|
||||
@@ -42,12 +100,14 @@ def main():
|
||||
settings['num_leds'] = 100
|
||||
print(f"Updated number of LEDs: {settings['num_leds']}")
|
||||
settings.save()
|
||||
|
||||
|
||||
# Create a new Settings object to test loading
|
||||
new_settings = Settings()
|
||||
print(f"Loaded number of LEDs: {new_settings['num_leds']}")
|
||||
print(settings)
|
||||
|
||||
|
||||
|
||||
# Run the example
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
@@ -1,75 +1,109 @@
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
form {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
input[type="text"], input[type="submit"], input[type="range"], input[type="color"] {
|
||||
width: 100%;
|
||||
|
||||
margin-bottom: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
input[type="range"] {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
height: 25px;
|
||||
background: #d3d3d3;
|
||||
outline: none;
|
||||
opacity: 0.7;
|
||||
transition: opacity .2s;
|
||||
}
|
||||
input[type="range"]:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
input[type="range"]::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
background: #4CAF50;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
}
|
||||
input[type="range"]::-moz-range-thumb {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
background: #4CAF50;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
}
|
||||
#pattern_buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
#pattern_buttons button {
|
||||
flex: 1 0 calc(33.333% - 10px);
|
||||
padding: 10px;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
#pattern_buttons button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
@media (max-width: 480px) {
|
||||
#pattern_buttons button {
|
||||
flex: 1 0 calc(50% - 10px);
|
||||
}
|
||||
}
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
form {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
input[type="text"],
|
||||
input[type="submit"],
|
||||
input[type="range"],
|
||||
input[type="color"] {
|
||||
width: 100%;
|
||||
|
||||
margin-bottom: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
input[type="range"] {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
height: 25px;
|
||||
background: #d3d3d3;
|
||||
outline: none;
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
input[type="range"]:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
input[type="range"]::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
background: #4caf50;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
}
|
||||
input[type="range"]::-moz-range-thumb {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
background: #4caf50;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
}
|
||||
#pattern_buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
#pattern_buttons button {
|
||||
flex: 1 0 calc(33.333% - 10px);
|
||||
padding: 10px;
|
||||
background-color: #4caf50;
|
||||
color: white;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
#pattern_buttons button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
@media (max-width: 480px) {
|
||||
#pattern_buttons button {
|
||||
flex: 1 0 calc(50% - 10px);
|
||||
}
|
||||
}
|
||||
#connection-status {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border-radius: 50%;
|
||||
display: inline-block; /* Or block, depending on where you put it */
|
||||
margin-left: 10px; /* Adjust spacing as needed */
|
||||
vertical-align: middle; /* Align with nearby text */
|
||||
background-color: grey; /* Default: Unknown */
|
||||
}
|
||||
|
||||
#connection-status.connecting {
|
||||
background-color: yellow;
|
||||
}
|
||||
|
||||
#connection-status.open {
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
#connection-status.closing,
|
||||
#connection-status.closed {
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
#color_order_form label,
|
||||
#color_order_form input[type="radio"] {
|
||||
/* Ensures they behave as inline elements */
|
||||
display: inline-block;
|
||||
/* Adds some space between them for readability */
|
||||
margin-right: 10px;
|
||||
vertical-align: middle; /* Aligns them nicely if heights vary */
|
||||
}
|
||||
|
@@ -2,146 +2,243 @@ let delayTimeout;
|
||||
let brightnessTimeout;
|
||||
let colorTimeout;
|
||||
let color2Timeout;
|
||||
let ws; // Variable to hold the WebSocket connection
|
||||
let connectionStatusElement; // Variable to hold the connection status element
|
||||
|
||||
async function post(path, data) {
|
||||
console.log(`POST to ${path}`, data);
|
||||
try {
|
||||
const response = await fetch(path, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(data) // Convert data to JSON string
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error during POST request:', error);
|
||||
// Function to update the connection status indicator
|
||||
function updateConnectionStatus(status) {
|
||||
if (!connectionStatusElement) {
|
||||
connectionStatusElement = document.getElementById("connection-status");
|
||||
}
|
||||
if (connectionStatusElement) {
|
||||
connectionStatusElement.className = ""; // Clear existing classes
|
||||
connectionStatusElement.classList.add(status);
|
||||
// Optionally, you could also update text content based on status
|
||||
// connectionStatusElement.textContent = status.charAt(0).toUpperCase() + status.slice(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to establish WebSocket connection
|
||||
function connectWebSocket() {
|
||||
// Determine the WebSocket URL based on the current location
|
||||
const wsUrl = `ws://${window.location.host}/ws`;
|
||||
ws = new WebSocket(wsUrl);
|
||||
|
||||
updateConnectionStatus("connecting"); // Indicate connecting state
|
||||
|
||||
ws.onopen = function (event) {
|
||||
console.log("WebSocket connection opened:", event);
|
||||
updateConnectionStatus("open"); // Indicate open state
|
||||
// Optionally, you could send an initial message here
|
||||
};
|
||||
|
||||
ws.onmessage = function (event) {
|
||||
console.log("WebSocket message received:", event.data);
|
||||
};
|
||||
|
||||
ws.onerror = function (event) {
|
||||
console.error("WebSocket error:", event);
|
||||
updateConnectionStatus("closed"); // Indicate error state (treat as closed)
|
||||
};
|
||||
|
||||
ws.onclose = function (event) {
|
||||
if (event.wasClean) {
|
||||
console.log(
|
||||
`WebSocket connection closed cleanly, code=${event.code}, reason=${event.reason}`,
|
||||
);
|
||||
updateConnectionStatus("closed"); // Indicate closed state
|
||||
} else {
|
||||
console.error("WebSocket connection died");
|
||||
updateConnectionStatus("closed"); // Indicate closed state
|
||||
}
|
||||
// Attempt to reconnect after a delay
|
||||
setTimeout(connectWebSocket, 1000);
|
||||
};
|
||||
}
|
||||
|
||||
// Function to send data over WebSocket
|
||||
function sendWebSocketData(data) {
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
console.log("Sending data over WebSocket:", data);
|
||||
ws.send(JSON.stringify(data));
|
||||
} else {
|
||||
console.error("WebSocket is not connected. Cannot send data:", data);
|
||||
// You might want to queue messages or handle this in a different way
|
||||
}
|
||||
}
|
||||
|
||||
// Keep the post and get functions for now, they might still be useful
|
||||
async function post(path, data) {
|
||||
console.log(`POST to ${path}`, data);
|
||||
try {
|
||||
const response = await fetch(path, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error during POST request:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function get(path) {
|
||||
try {
|
||||
const response = await fetch(path);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
}
|
||||
return await response.json(); // Assuming you are expecting JSON response
|
||||
} catch (error) {
|
||||
console.error('Error during GET request:', error);
|
||||
try {
|
||||
const response = await fetch(path);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
}
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error("Error during GET request:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function updateColor(event) {
|
||||
event.preventDefault();
|
||||
clearTimeout(colorTimeout);
|
||||
colorTimeout = setTimeout(async function() {
|
||||
const color = document.getElementById('color').value;
|
||||
await post("/color", { color }); // Send as JSON
|
||||
}, 500);
|
||||
event.preventDefault();
|
||||
clearTimeout(colorTimeout);
|
||||
colorTimeout = setTimeout(function () {
|
||||
const color = document.getElementById("color").value;
|
||||
sendWebSocketData({ color1: color });
|
||||
}, 500);
|
||||
}
|
||||
|
||||
async function updateColor2(event) {
|
||||
event.preventDefault();
|
||||
clearTimeout(color2Timeout);
|
||||
color2Timeout = setTimeout(async function() {
|
||||
const color = document.getElementById('color2').value;
|
||||
await post("/color2", { color }); // Send as JSON
|
||||
}, 500);
|
||||
event.preventDefault();
|
||||
clearTimeout(color2Timeout);
|
||||
color2Timeout = setTimeout(function () {
|
||||
const color = document.getElementById("color2").value;
|
||||
sendWebSocketData({ color2: color });
|
||||
}, 500);
|
||||
}
|
||||
|
||||
async function updatePattern(pattern) {
|
||||
event.preventDefault();
|
||||
await post("/pattern", { pattern }); // Send as JSON
|
||||
sendWebSocketData({ pattern: pattern });
|
||||
}
|
||||
|
||||
async function updateBrightness(event) {
|
||||
event.preventDefault();
|
||||
clearTimeout(brightnessTimeout);
|
||||
brightnessTimeout = setTimeout(async function() {
|
||||
const brightness = document.getElementById('brightness').value;
|
||||
await post('/brightness', { brightness }); // Send as JSON
|
||||
}, 500);
|
||||
event.preventDefault();
|
||||
clearTimeout(brightnessTimeout);
|
||||
brightnessTimeout = setTimeout(function () {
|
||||
const brightness = document.getElementById("brightness").value;
|
||||
sendWebSocketData({ brightness: brightness });
|
||||
}, 500);
|
||||
}
|
||||
|
||||
async function updateDelay(event) {
|
||||
event.preventDefault();
|
||||
clearTimeout(delayTimeout);
|
||||
delayTimeout = setTimeout(async function() {
|
||||
const delay = document.getElementById('delay').value;
|
||||
await post('/delay', { delay }); // Send as JSON
|
||||
}, 500);
|
||||
event.preventDefault();
|
||||
clearTimeout(delayTimeout);
|
||||
delayTimeout = setTimeout(function () {
|
||||
const delay = document.getElementById("delay").value;
|
||||
sendWebSocketData({ delay: delay });
|
||||
}, 500);
|
||||
}
|
||||
|
||||
async function updateNumLeds(event) {
|
||||
event.preventDefault();
|
||||
const numLeds = document.getElementById('num_leds').value;
|
||||
await post('/num_leds', { num_leds: numLeds }); // Send as JSON
|
||||
event.preventDefault();
|
||||
const numLeds = document.getElementById("num_leds").value;
|
||||
sendWebSocketData({ num_leds: parseInt(numLeds) });
|
||||
}
|
||||
|
||||
async function updateWifi(event) {
|
||||
event.preventDefault();
|
||||
const ssid = document.getElementById('ssid').value;
|
||||
const password = document.getElementById('password').value;
|
||||
const ip = document.getElementById('ip').value;
|
||||
const gateway = document.getElementById('gateway').value;
|
||||
async function updateName(event) {
|
||||
event.preventDefault();
|
||||
const name = document.getElementById("name").value;
|
||||
sendWebSocketData({ name: name });
|
||||
}
|
||||
|
||||
const wifiSettings = { ssid, password, ip, gateway }; // Create JSON object
|
||||
console.log(wifiSettings);
|
||||
const response = await post('/wifi_settings', wifiSettings); // Send as JSON
|
||||
if (response === 500) {
|
||||
alert("Failed to connect to Wi-Fi");
|
||||
}
|
||||
async function updateID(event) {
|
||||
event.preventDefault();
|
||||
const id = document.getElementById("id").value;
|
||||
sendWebSocketData({ id: parseInt(id) });
|
||||
}
|
||||
|
||||
async function updateLedPin(event) {
|
||||
event.preventDefault();
|
||||
const ledpin = document.getElementById("led_pin").value;
|
||||
sendWebSocketData({ led_pin: parseInt(ledpin) });
|
||||
}
|
||||
|
||||
function handleRadioChange(event) {
|
||||
event.preventDefault();
|
||||
console.log("Selected color order:", event.target.value);
|
||||
// Add your specific logic here
|
||||
if (event.target.value === "rgb") {
|
||||
console.log("RGB order selected!");
|
||||
} else if (event.target.value === "rbg") {
|
||||
console.log("RBG order selected!");
|
||||
}
|
||||
sendWebSocketData({ color_order: event.target.value });
|
||||
}
|
||||
|
||||
function createPatternButtons(patterns) {
|
||||
const container = document.getElementById('pattern_buttons');
|
||||
container.innerHTML = ''; // Clear previous buttons
|
||||
const container = document.getElementById("pattern_buttons");
|
||||
container.innerHTML = ""; // Clear previous buttons
|
||||
|
||||
patterns.forEach(pattern => {
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button'; // Use 'button' instead of 'submit'
|
||||
button.textContent = pattern;
|
||||
button.value = pattern;
|
||||
button.addEventListener('click', async function(event) {
|
||||
event.preventDefault();
|
||||
await updatePattern(pattern);
|
||||
});
|
||||
container.appendChild(button);
|
||||
patterns.forEach((pattern) => {
|
||||
const button = document.createElement("button");
|
||||
button.type = "button";
|
||||
button.textContent = pattern;
|
||||
button.value = pattern;
|
||||
button.addEventListener("click", async function (event) {
|
||||
event.preventDefault();
|
||||
await updatePattern(pattern);
|
||||
});
|
||||
container.appendChild(button);
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async function() {
|
||||
document.getElementById('color').addEventListener('input', updateColor);
|
||||
document.getElementById('color2').addEventListener('input', updateColor2);
|
||||
document.getElementById('delay').addEventListener('input', updateDelay);
|
||||
document.getElementById('brightness').addEventListener('input', updateBrightness);
|
||||
document.getElementById('num_leds_form').addEventListener('submit', updateNumLeds);
|
||||
document.getElementById('wifi_form').addEventListener('submit', updateWifi);
|
||||
document.getElementById('delay').addEventListener('touchend', updateDelay);
|
||||
document.getElementById('brightness').addEventListener('touchend', updateBrightness);
|
||||
document.addEventListener("DOMContentLoaded", async function () {
|
||||
// Get the connection status element once the DOM is ready
|
||||
connectionStatusElement = document.getElementById("connection-status");
|
||||
|
||||
document.querySelectorAll(".pattern_button").forEach(button => {
|
||||
console.log(button.value);
|
||||
button.addEventListener('click', async event => {
|
||||
event.preventDefault();
|
||||
await updatePattern(button.value);
|
||||
});
|
||||
});
|
||||
// Establish WebSocket connection on page load
|
||||
connectWebSocket();
|
||||
|
||||
document.getElementById("color").addEventListener("input", updateColor);
|
||||
document.getElementById("color2").addEventListener("input", updateColor2);
|
||||
document.getElementById("delay").addEventListener("input", updateDelay);
|
||||
document
|
||||
.getElementById("brightness")
|
||||
.addEventListener("input", updateBrightness);
|
||||
document
|
||||
.getElementById("num_leds_form")
|
||||
.addEventListener("submit", updateNumLeds);
|
||||
document.getElementById("name_form").addEventListener("submit", updateName);
|
||||
document.getElementById("id_form").addEventListener("submit", updateID);
|
||||
document
|
||||
.getElementById("led_pin_form")
|
||||
.addEventListener("submit", updateLedPin);
|
||||
document.getElementById("delay").addEventListener("touchend", updateDelay);
|
||||
document
|
||||
.getElementById("brightness")
|
||||
.addEventListener("touchend", updateBrightness);
|
||||
|
||||
document.getElementById("rgb").addEventListener("change", handleRadioChange);
|
||||
document.getElementById("rbg").addEventListener("change", handleRadioChange);
|
||||
document.querySelectorAll(".pattern_button").forEach((button) => {
|
||||
console.log(button.value);
|
||||
button.addEventListener("click", async (event) => {
|
||||
event.preventDefault();
|
||||
await updatePattern(button.value);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Function to toggle the display of the settings menu
|
||||
function selectSettings() {
|
||||
const settingsMenu = document.getElementById('settings_menu');
|
||||
controls = document.getElementById('controls');
|
||||
settingsMenu.style.display = 'block';
|
||||
controls.style.display = 'none';
|
||||
const settingsMenu = document.getElementById("settings_menu");
|
||||
controls = document.getElementById("controls");
|
||||
settingsMenu.style.display = "block";
|
||||
controls.style.display = "none";
|
||||
}
|
||||
|
||||
function selectControls() {
|
||||
const settingsMenu = document.getElementById('settings_menu');
|
||||
controls = document.getElementById('controls');
|
||||
settingsMenu.style.display = 'none';
|
||||
controls.style.display = 'block';
|
||||
const settingsMenu = document.getElementById("settings_menu");
|
||||
controls = document.getElementById("controls");
|
||||
settingsMenu.style.display = "none";
|
||||
controls.style.display = "block";
|
||||
}
|
||||
|
@@ -1,71 +1,124 @@
|
||||
{% args settings, patterns %}
|
||||
<!DOCTYPE html>
|
||||
{% args settings, patterns, mac %}
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>LED Control</title>
|
||||
<script src="static/main.js"></script>
|
||||
<link rel="stylesheet" href="static/main.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Control LEDs</h1>
|
||||
<button onclick="selectControls()">Controls</button>
|
||||
<button onclick="selectSettings()">Settings</button>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>{{settings['name']}}</title>
|
||||
<script src="static/main.js"></script>
|
||||
<link rel="stylesheet" href="static/main.css" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{settings['name']}}</h1>
|
||||
<button onclick="selectControls()">Controls</button>
|
||||
<button onclick="selectSettings()">Settings</button>
|
||||
|
||||
<!-- Main LED Controls -->
|
||||
<div id="controls">
|
||||
<div id="pattern_buttons">
|
||||
{% for p in patterns %}
|
||||
<button class="pattern_button" value="{{p}}">{{p}}</button>
|
||||
{% endfor %}
|
||||
|
||||
<!-- Pattern buttons will be inserted here -->
|
||||
<!-- Main LED Controls -->
|
||||
<div id="controls">
|
||||
<div id="pattern_buttons">
|
||||
{% for p in patterns %}
|
||||
<button class="pattern_button" value="{{p}}">{{p}}</button>
|
||||
{% endfor %}
|
||||
|
||||
<!-- Pattern buttons will be inserted here -->
|
||||
</div>
|
||||
<form id="delay_form" method="post" action="/delay">
|
||||
<label for="delay">Delay:</label>
|
||||
<input
|
||||
type="range"
|
||||
id="delay"
|
||||
name="delay"
|
||||
min="1"
|
||||
max="1000"
|
||||
value="{{settings['delay']}}"
|
||||
step="10"
|
||||
/>
|
||||
</form>
|
||||
<form id="brightness_form" method="post" action="/brightness">
|
||||
<label for="brightness">Brightness:</label>
|
||||
<input
|
||||
type="range"
|
||||
id="brightness"
|
||||
name="brightness"
|
||||
min="0"
|
||||
max="100"
|
||||
value="{{settings['brightness']}}"
|
||||
step="1"
|
||||
/>
|
||||
</form>
|
||||
<form id="color_form" method="post" action="/color">
|
||||
<input
|
||||
type="color"
|
||||
id="color"
|
||||
name="color"
|
||||
value="{{settings['color1']}}"
|
||||
/>
|
||||
</form>
|
||||
<form id="color2_form" method="post" action="/color2">
|
||||
<input
|
||||
type="color"
|
||||
id="color2"
|
||||
name="color2"
|
||||
value="{{settings['color2']}}"
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
<form id="delay_form" method="post" action="/delay">
|
||||
<label for="delay">Delay:</label>
|
||||
<input type="range" id="delay" name="delay" min="1" max="1000" value="{{settings['delay']}}" step="10">
|
||||
</form>
|
||||
<form id="brightness_form" method="post" action="/brightness">
|
||||
<label for="brightness">Brightness:</label>
|
||||
<input type="range" id="brightness" name="brightness" min="0" max="100" value="{{settings['brightness']}}" step="1">
|
||||
</form>
|
||||
<form id="color_form" method="post" action="/color">
|
||||
<input type="color" id="color" name="color" value="{{settings['color1']}}">
|
||||
</form>
|
||||
<form id="color2_form" method="post" action="/color2">
|
||||
<input type="color" id="color2" name="color2" value="{{settings['color2']}}">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Settings Menu for num_leds, Wi-Fi SSID, and Password -->
|
||||
|
||||
<div id="settings_menu" style="display: none;">
|
||||
<h2>Settings</h2>
|
||||
|
||||
<!-- Separate form for submitting num_leds -->
|
||||
<form id="num_leds_form" method="post" action="/num_leds">
|
||||
<label for="num_leds">Number of LEDs:</label>
|
||||
<input type="text" id="num_leds" name="num_leds" value="{{settings['num_leds']}}">
|
||||
<input type="submit" value="Update Number of LEDs">
|
||||
</form>
|
||||
<!-- Settings Menu for num_leds, Wi-Fi SSID, and Password -->
|
||||
|
||||
<!-- Form for Wi-Fi SSID and password -->
|
||||
<form id="wifi_form" method="post" action="/wifi_settings">
|
||||
<label for="ssid">Wi-Fi SSID:</label>
|
||||
<input type="text" id="ssid" name="ssid" value="{{settings['wifi']['ssid']}}">
|
||||
<br>
|
||||
<label for="password">Wi-Fi Password:</label>
|
||||
<input type="password" id="password" name="password">
|
||||
<br>
|
||||
<label for="ip">Wi-Fi IP:</label>
|
||||
<input type="ip" id="ip" name="ip" value="{{settings.get('wifi', {}).get('ip', '')}}">
|
||||
<br>
|
||||
<label for="gateway">Wi-Fi Gateway:</label>
|
||||
<input type="gateway" id="gateway" name="gateway" value="{{settings.get('wifi', {}).get('gateway', '')}}">
|
||||
<br>
|
||||
<input type="submit" value="Save Wi-Fi Settings">
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
<div id="settings_menu" style="display: none">
|
||||
<h2>Settings</h2>
|
||||
|
||||
<form id="name_form" method="post" action="/name">
|
||||
<label for="name">Name:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
name="num_leds"
|
||||
value="{{settings['name']}}"
|
||||
/>
|
||||
<input type="submit" value="Update Name" />
|
||||
</form>
|
||||
<form id="id_form" method="post" action="/id">
|
||||
<label for="id">ID:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="id"
|
||||
name="id"
|
||||
value="{{settings['id']}}"
|
||||
/>
|
||||
<input type="submit" value="Update ID" />
|
||||
</form>
|
||||
<!-- Separate form for submitting num_leds -->
|
||||
<form id="num_leds_form" method="post" action="/num_leds">
|
||||
<label for="num_leds">Number of LEDs:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="num_leds"
|
||||
name="num_leds"
|
||||
value="{{settings['num_leds']}}"
|
||||
/>
|
||||
<input type="submit" value="Update Number of LEDs" />
|
||||
</form>
|
||||
<form id="led_pin_form" method="post" action="/led_pin">
|
||||
<label for="num_leds">Led pin:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="led_pin"
|
||||
name="led_pin"
|
||||
value="{{settings['led_pin']}}"
|
||||
/>
|
||||
<input type="submit" value="Update Led Pin" />
|
||||
</form>
|
||||
<form id="color_order_form">
|
||||
<label for="rgb">RGB:</label>
|
||||
<input type="radio" id="rgb" name="color_order" value="rgb" {{'checked' if settings["color_order"]=="rgb" else ''}} />
|
||||
<label for="rbg">RBG</label>
|
||||
<input type="radio" id="rbg" name="color_order" value="rbg" {{'checked' if settings["color_order"]=="rbg" else ''}}/>
|
||||
</form>
|
||||
|
||||
<p>Mac address: {{mac}}</p>
|
||||
</div>
|
||||
<div id="connection-status"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -1,94 +0,0 @@
|
||||
# Autogenerated file
|
||||
def render(settings, patterns):
|
||||
yield """<!DOCTYPE html>
|
||||
<html lang=\"en\">
|
||||
<head>
|
||||
<meta charset=\"UTF-8\">
|
||||
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">
|
||||
<title>LED Control</title>
|
||||
<script src=\"static/main.js\"></script>
|
||||
<link rel=\"stylesheet\" href=\"static/main.css\">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Control LEDs</h1>
|
||||
<button onclick=\"selectControls()\">Controls</button>
|
||||
<button onclick=\"selectSettings()\">Settings</button>
|
||||
|
||||
<!-- Main LED Controls -->
|
||||
<div id=\"controls\">
|
||||
<div id=\"pattern_buttons\">
|
||||
"""
|
||||
for p in patterns:
|
||||
yield """ <button class=\"pattern_button\" value=\""""
|
||||
yield str(p)
|
||||
yield """\">"""
|
||||
yield str(p)
|
||||
yield """</button>
|
||||
"""
|
||||
yield """
|
||||
<!-- Pattern buttons will be inserted here -->
|
||||
</div>
|
||||
<form id=\"delay_form\" method=\"post\" action=\"/delay\">
|
||||
<label for=\"delay\">Delay:</label>
|
||||
<input type=\"range\" id=\"delay\" name=\"delay\" min=\"1\" max=\"1000\" value=\""""
|
||||
yield str(settings['delay'])
|
||||
yield """\" step=\"10\">
|
||||
</form>
|
||||
<form id=\"brightness_form\" method=\"post\" action=\"/brightness\">
|
||||
<label for=\"brightness\">Brightness:</label>
|
||||
<input type=\"range\" id=\"brightness\" name=\"brightness\" min=\"0\" max=\"100\" value=\""""
|
||||
yield str(settings['brightness'])
|
||||
yield """\" step=\"1\">
|
||||
</form>
|
||||
<form id=\"color_form\" method=\"post\" action=\"/color\">
|
||||
<input type=\"color\" id=\"color\" name=\"color\" value=\""""
|
||||
yield str(settings['color1'])
|
||||
yield """\">
|
||||
</form>
|
||||
<form id=\"color2_form\" method=\"post\" action=\"/color2\">
|
||||
<input type=\"color\" id=\"color2\" name=\"color2\" value=\""""
|
||||
yield str(settings['color2'])
|
||||
yield """\">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Settings Menu for num_leds, Wi-Fi SSID, and Password -->
|
||||
|
||||
<div id=\"settings_menu\" style=\"display: none;\">
|
||||
<h2>Settings</h2>
|
||||
|
||||
<!-- Separate form for submitting num_leds -->
|
||||
<form id=\"num_leds_form\" method=\"post\" action=\"/num_leds\">
|
||||
<label for=\"num_leds\">Number of LEDs:</label>
|
||||
<input type=\"text\" id=\"num_leds\" name=\"num_leds\" value=\""""
|
||||
yield str(settings['num_leds'])
|
||||
yield """\">
|
||||
<input type=\"submit\" value=\"Update Number of LEDs\">
|
||||
</form>
|
||||
|
||||
<!-- Form for Wi-Fi SSID and password -->
|
||||
<form id=\"wifi_form\" method=\"post\" action=\"/wifi_settings\">
|
||||
<label for=\"ssid\">Wi-Fi SSID:</label>
|
||||
<input type=\"text\" id=\"ssid\" name=\"ssid\" value=\""""
|
||||
yield str(settings['wifi']['ssid'])
|
||||
yield """\">
|
||||
<br>
|
||||
<label for=\"password\">Wi-Fi Password:</label>
|
||||
<input type=\"password\" id=\"password\" name=\"password\">
|
||||
<br>
|
||||
<label for=\"ip\">Wi-Fi IP:</label>
|
||||
<input type=\"ip\" id=\"ip\" name=\"ip\" value=\""""
|
||||
yield str(settings.get('wifi', {}).get('ip', ''))
|
||||
yield """\">
|
||||
<br>
|
||||
<label for=\"gateway\">Wi-Fi Gateway:</label>
|
||||
<input type=\"gateway\" id=\"gateway\" name=\"gateway\" value=\""""
|
||||
yield str(settings.get('wifi', {}).get('gateway', ''))
|
||||
yield """\">
|
||||
<br>
|
||||
<input type=\"submit\" value=\"Save Wi-Fi Settings\">
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
130
src/web.py
130
src/web.py
@@ -1,135 +1,43 @@
|
||||
from microdot import Microdot, send_file, Response
|
||||
from microdot.utemplate import Template
|
||||
from microdot.websocket import with_websocket
|
||||
|
||||
import json
|
||||
import machine
|
||||
import wifi
|
||||
import json
|
||||
|
||||
def web(settings, patterns):
|
||||
app = Microdot()
|
||||
Response.default_content_type = 'text/html'
|
||||
|
||||
@app.route('/')
|
||||
async def index(request):
|
||||
return Template('/index.html').render(settings=settings, patterns=patterns.patterns.keys())
|
||||
async def index_hnadler(request):
|
||||
mac = wifi.get_mac().hex()
|
||||
return Template('/index.html').render(settings=settings, patterns=patterns.patterns.keys(), mac=mac)
|
||||
|
||||
@app.route("/static/<path:path>")
|
||||
def static(request, path):
|
||||
def static_handler(request, path):
|
||||
if '..' in path:
|
||||
# Directory traversal is not allowed
|
||||
return 'Not found', 404
|
||||
return send_file('static/' + path)
|
||||
|
||||
@app.post("/num_leds")
|
||||
def num_leds(request):
|
||||
try:
|
||||
data = json.loads(request.body.decode('utf-8'))
|
||||
num_leds = int(data["num_leds"])
|
||||
patterns.update_num_leds(4, num_leds)
|
||||
settings["num_leds"] = num_leds
|
||||
settings.save()
|
||||
return "OK", 200
|
||||
except (ValueError, KeyError, json.JSONDecodeError):
|
||||
return "Bad request", 400
|
||||
@app.post("/settings")
|
||||
def settings_handler(request):
|
||||
# Keep the POST handler for compatibility or alternative usage if needed
|
||||
# For WebSocket updates, the /ws handler is now primary
|
||||
return settings.set_settings(request.body.decode('utf-8'), patterns)
|
||||
|
||||
@app.post("/pattern")
|
||||
def pattern(request):
|
||||
try:
|
||||
data = json.loads(request.body.decode('utf-8'))
|
||||
pattern = data["pattern"]
|
||||
if patterns.select(pattern):
|
||||
settings["selected_pattern"] = pattern
|
||||
settings.save()
|
||||
return "OK", 200
|
||||
else:
|
||||
return "Bad request", 400
|
||||
except (KeyError, json.JSONDecodeError):
|
||||
return "Bad request", 400
|
||||
|
||||
@app.post("/delay")
|
||||
def delay(request):
|
||||
try:
|
||||
data = json.loads(request.body.decode('utf-8'))
|
||||
delay = int(data["delay"])
|
||||
patterns.set_delay(delay)
|
||||
settings["delay"] = delay
|
||||
settings.save()
|
||||
return "OK", 200
|
||||
except (ValueError, KeyError, json.JSONDecodeError):
|
||||
return "Bad request", 400
|
||||
|
||||
@app.post("/brightness")
|
||||
def brightness(request):
|
||||
try:
|
||||
data = json.loads(request.body.decode('utf-8'))
|
||||
brightness = int(data["brightness"])
|
||||
patterns.set_brightness(brightness)
|
||||
settings["brightness"] = brightness
|
||||
settings.save()
|
||||
return "OK", 200
|
||||
except (ValueError, KeyError, json.JSONDecodeError):
|
||||
return "Bad request", 400
|
||||
|
||||
@app.post("/color")
|
||||
def color(request):
|
||||
try:
|
||||
data = json.loads(request.body.decode('utf-8'))
|
||||
color = data["color"]
|
||||
patterns.set_color1(tuple(int(color[i:i+2], 16) for i in (1, 3, 5))) # Convert hex to RGB
|
||||
settings["color1"] = color
|
||||
settings.save()
|
||||
return "OK", 200
|
||||
except (KeyError, json.JSONDecodeError, ValueError):
|
||||
return "Bad request", 400
|
||||
|
||||
@app.post("/color2")
|
||||
def color2(request):
|
||||
try:
|
||||
data = json.loads(request.body.decode('utf-8'))
|
||||
color = data["color2"]
|
||||
patterns.set_color2(tuple(int(color[i:i+2], 16) for i in (1, 3, 5))) # Convert hex to RGB
|
||||
settings["color2"] = color
|
||||
settings.save()
|
||||
return "OK", 200
|
||||
except (KeyError, json.JSONDecodeError, ValueError):
|
||||
return "Bad request", 400
|
||||
|
||||
|
||||
@app.post("/wifi_settings")
|
||||
def wifi_settings(request):
|
||||
try:
|
||||
data = json.loads(request.body.decode('utf-8'))
|
||||
print(data)
|
||||
ssid = settings['wifi']['ssid'] = data['ssid']
|
||||
password = settings['wifi']['password'] = data.get('password', settings['wifi']['password'])
|
||||
ip = settings['wifi']['ip'] = data.get('ip', None)
|
||||
gateway = settings['wifi']['gateway'] = data.get('gateway', None)
|
||||
print(settings)
|
||||
|
||||
if config := wifi.connect(ssid, password, ip, gateway):
|
||||
print(config)
|
||||
settings.save()
|
||||
|
||||
return "OK", 200
|
||||
except Exception as e:
|
||||
print(f"Wifi {e}")
|
||||
return "Bad request", 400
|
||||
|
||||
|
||||
@app.post("/sync")
|
||||
def sync(request):
|
||||
patterns.sync()
|
||||
return "OK", 200
|
||||
|
||||
@app.route("/external")
|
||||
@app.route("/ws")
|
||||
@with_websocket
|
||||
async def ws(request, ws):
|
||||
patterns.select("external")
|
||||
while True:
|
||||
data = await ws.receive()
|
||||
print(data)
|
||||
for i in range(min(patterns.num_leds, int(len(data)/3))):
|
||||
patterns.set(i, (data[i*3], data[i*3+1], data[i*3+2]))
|
||||
patterns.write()
|
||||
if data:
|
||||
|
||||
# Process the received data
|
||||
_, status_code = settings.set_settings(json.loads(data), patterns, True)
|
||||
#await ws.send(status_code)
|
||||
else:
|
||||
break
|
||||
|
||||
return app
|
||||
|
29
src/wifi.py
29
src/wifi.py
@@ -1,18 +1,16 @@
|
||||
import network
|
||||
from machine import Pin
|
||||
from time import sleep
|
||||
import ubinascii
|
||||
from settings import Settings
|
||||
|
||||
def connect(ssid, password, ip, gateway):
|
||||
if ssid is None or password is None:
|
||||
print("Missing ssid or password")
|
||||
return None
|
||||
|
||||
try:
|
||||
sta_if = network.WLAN(network.STA_IF)
|
||||
if ip is not None and gateway is not None:
|
||||
sta_if.ifconfig((ip, '255.255.255.0', gateway, '1.1.1.1'))
|
||||
if not sta_if.isconnected():
|
||||
if ssid == "" or password == "":
|
||||
print("Missing ssid or password")
|
||||
return None
|
||||
if ip != "" and gateway != "":
|
||||
sta_if.ifconfig((ip, '255.255.255.0', gateway, '1.1.1.1'))
|
||||
print('connecting to network...')
|
||||
sta_if.active(True)
|
||||
sta_if.connect(ssid, password)
|
||||
@@ -26,21 +24,16 @@ def connect(ssid, password, ip, gateway):
|
||||
return None
|
||||
|
||||
|
||||
def ap(password):
|
||||
def ap(ssid, password):
|
||||
ap_if = network.WLAN(network.AP_IF)
|
||||
ap_mac = ap_if.config('mac')
|
||||
ssid = f"led-{ubinascii.hexlify(ap_mac).decode()}"
|
||||
print(ssid)
|
||||
ap_if.active(True)
|
||||
ap_if.config(essid=ssid, password="qwerty1234")
|
||||
ap_if.config(essid=ssid, password=password)
|
||||
ap_if.active(False)
|
||||
ap_if.active(True)
|
||||
print(ap_if.ifconfig())
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def get_mac():
|
||||
ap_if = network.WLAN(network.AP_IF)
|
||||
return ap_if.config('mac')
|
||||
|
Reference in New Issue
Block a user