Compare commits
52 Commits
31081d98ee
...
patterns-a
Author | SHA1 | Date | |
---|---|---|---|
e83f0d607c | |||
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
|
||||
|
359
Pipfile.lock
generated
359
Pipfile.lock
generated
@@ -18,161 +18,158 @@
|
||||
"default": {
|
||||
"argcomplete": {
|
||||
"hashes": [
|
||||
"sha256:2ab2c4a215c59fd6caaff41a869480a23e8f6a5f910b266c1808037f4e375b61",
|
||||
"sha256:c12bf50eded8aebb298c7b7da7a5ff3ee24dffd9f5281867dfe1424b58c55392"
|
||||
"sha256:65b3133a29ad53fb42c48cf5114752c7ab66c1c38544fdf6460f450c09b42591",
|
||||
"sha256:d0519b1bc867f5f4f4713c41ad0aba73a4a5f007449716b16f385f2166dc6adf"
|
||||
],
|
||||
"markers": "sys_platform != 'win32'",
|
||||
"version": "==3.5.3"
|
||||
"version": "==3.6.2"
|
||||
},
|
||||
"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:01299fb36af3e7955967f3dbc4097a2d88845166837899350f411d95a857f8aa",
|
||||
"sha256:0580b905ad589e3be52d36fbc83d32f6e3f6a63751d6c0da0ca328c32d037790",
|
||||
"sha256:0952d05e1d6b0a736d73d34128b652d7549ba7d00ccc1e7c00efbc6edd687ee3",
|
||||
"sha256:0b7e1f4139d3f17feba72e386a8f1318fb35182ff65890281e727fd07fdfbd72",
|
||||
"sha256:0ba347a4dcc990637aa700227675d8033f68b417dcd7ccf660bd2e87e10885ec",
|
||||
"sha256:0d11e1a8914321fac34f50c48a9b1f92a1f51f45f9beb23e990806588137c4ca",
|
||||
"sha256:131ff1eed8902fb54ea64f8d0bf8fcbbda8ad6b9639d81cacc3a398c7488fecb",
|
||||
"sha256:14f04e4eec65891523a8ca3bf9e1dcdefed52d695f40c4e50d5980471ffd22a4",
|
||||
"sha256:176991b2769425341da4d52a684795498c0cd4136f4329ba9d524bcb96d26604",
|
||||
"sha256:1b7e89d4005eee831dc90d50c69af74ece6088f3c1b673d0089c8ef7d5346c37",
|
||||
"sha256:1ce3e352f1b7f1201b04600f93035312b00c9f8f4d606048c39adac32b2fb738",
|
||||
"sha256:24296caffe89af65fc8029a56274db6a268f6a297a5163e65df8177c2dd67b19",
|
||||
"sha256:2441da551787086c57fa8983d43e103fd2519389c8e03302908697138c287d6a",
|
||||
"sha256:26a26614bba95f3e4ea8c285206a4efe5ffb99e8539356d78a62491facc326cf",
|
||||
"sha256:28d866fa462d77cafbf284aea14102a31dcfdebb9a5abbfb453f6eb6b2deb4fd",
|
||||
"sha256:2e92d2d7d405e004f2bdf9ff6d58faed6d04e0b74a9d96905ade61c293abe315",
|
||||
"sha256:2fbd399cfdb7dee0bb4705bc8cd51163a9b2f25bb266807d57e5c693e0a14df2",
|
||||
"sha256:307e4cd6b94de4b4b5b0f4599ffddabde4c33ac22a74998887048d24cb379ad3",
|
||||
"sha256:31f21c7df3b40db541182db500f96cf2b9688261baec7b03a6010fdfc5e31855",
|
||||
"sha256:36851e3244950adc75670354dcd9bcad65e1695933c18762bb6f7590734c14ef",
|
||||
"sha256:39fdd56fd9076a4a34c3cd21e1c84dc861dac5e92c1ed9daed6aed6b11719c8c",
|
||||
"sha256:3afe39028afff6e94bb90eb0f8c5eb9357c0e37ce3c249f96dbcfc1a73938015",
|
||||
"sha256:3fcdaf79970b41cfe21b6cf6a7bbe2d0f17e3371a4d839f1279283ac03dd2a47",
|
||||
"sha256:421da43706c9a01d1b1454c34edbff372a7cfeff33879b6c048fc5f4481a9454",
|
||||
"sha256:42376c9e0a1357acc8830c4c0267e1c30ebd04b2d822af702044962a9f30b795",
|
||||
"sha256:434180c1340268763439b80d21e074df24633c8748a867573bafecdbfaa68a76",
|
||||
"sha256:434e389958ab98415ed4d9d67dd94c0ac835036a16b488df6736222f4f55ff35",
|
||||
"sha256:47abbec73f20176e119f5c4c68aaf243c46a5e072b9c182f2c110b5b227256a7",
|
||||
"sha256:492524a28c3aab6a4ef0a741ee9f3578b6606bb52a7a94106c386bdebab1df44",
|
||||
"sha256:4bb2fa914a7bbcd7c6a457d44461a8540b9450e9bb4163d734eb74bffba90e69",
|
||||
"sha256:4bda4e4219c6271beec737a5361b009dcf9ff6d84a2df92bf3dd4f4e97bb87e5",
|
||||
"sha256:4c516daf790bd870d7575ac0e4136f1c3bc180b0de2a6bfa9fa112ea668131a0",
|
||||
"sha256:4ddef0b620db43dfde43fe17448ddc37289f67ad9a8ae39ffa64fa7bf529145f",
|
||||
"sha256:50da5ecd86ee25df9f658d8724efbe8060de97217fb12a1163bee61d42946d83",
|
||||
"sha256:50df8e1915a1acfd9cd0a4657d26cacd5aee4c3286ebb63e9dd75271ea6b54e0",
|
||||
"sha256:518e04584654a155fca829a6fe847cd403a17007e5afdc2b05b4240b53cd0842",
|
||||
"sha256:51ce410a2d91da4b98d0f043df9e0938c33a2d9ad4a370fa8ec1ce7352fc20d9",
|
||||
"sha256:52e8d36933bb3fb132c95c43171f47f07c22dd31536495be20f86ddbf383e3c6",
|
||||
"sha256:52edf707f2fddb6a60a20093c3051c1925830d8c4e7fb2692aac2ee970cee2b0",
|
||||
"sha256:535cc398610ff22dc0341e8833c34be73634a9a0a5d04912b4044e91dfbbc413",
|
||||
"sha256:54093229fec0f8c605b7873020c07681c1f1f96c433ae082d2da106ab11b206f",
|
||||
"sha256:54ac6f8d2f696d83f9ccbb4cc4ce321dc80b9fa4613749a8ab23bda5674510ea",
|
||||
"sha256:5500052aaf761afede3763434097a59042e22fbde508c88238d34105c13564c0",
|
||||
"sha256:551844744d22fe2e37525bd7132d2e9dae5a9621e3d8a43f46bbe6edadb4c63b",
|
||||
"sha256:58365c6c3e4a5ebbc8f28bf7764f5b00be5c8b1ffbd70474e6f801383f3fe0a0",
|
||||
"sha256:5aacbf54ad69248e17aab92a9f2d8a0a7efaea9d5401207cb9dac41d46294d56",
|
||||
"sha256:5d47d349468177afbe77e5306e70fd131d8da6946dd22ed93cbe70c5f2965307",
|
||||
"sha256:5dcb5aaaa2d91cc04fa9adfe31222ab150e72d99c779b1ddca10400a2fd319ec",
|
||||
"sha256:601fedd0e5227a5591e2eae2d35d45a07f030783fc41fd217cdf0c74db554cb9",
|
||||
"sha256:62c2278763edc823e79b8f0a0fdc7c8c9c45a3e982db9355042839c1f0c4ea92",
|
||||
"sha256:638ad50ecbffd05efdfa9f77b24b497b8e523f078315846614c647ebc3389bb5",
|
||||
"sha256:64a5404a258ef903db67d7911147febf112858ba30c180dae0c23405412e0a2f",
|
||||
"sha256:64cef9f2d15261ea667838a4460f75acf4b03d64d53df664357541cc8d2c8183",
|
||||
"sha256:653d56c58197940f0c1305cb474b75597421b424be99284915bb4f3529d51837",
|
||||
"sha256:673a21ebb6c72904d7de58fe8c557bad614fce773f21ddc86bcf8dd09a387a32",
|
||||
"sha256:69679fcd5f2c4b7c8920d2824519e3bff81a18fac25acf33ded4524ea68d8a39",
|
||||
"sha256:71838052ad546da110b8a8aaa254bda2e162e65af563d92b15c8bc7ab1642909",
|
||||
"sha256:7445c34e5d55ec512447efa746f046ecf4627c08281fc6e9ef844423167237bc",
|
||||
"sha256:751a2cd05326e1552b56090595ba8d35fe6fef666d5ca9c0a26d329c65a9c4a0",
|
||||
"sha256:75eb4d353dcf571d98e2818119af303fb0181b54361ac9a3e418b31c08131e56",
|
||||
"sha256:76abaeac4f94eda1755eed633a720c1f5f90048cb7ea4ab217ea84c48414189a",
|
||||
"sha256:78d069a00a8d06fb68248edd5bf2aa5e8009f4f5eae8dd5b5a529812132ad8a6",
|
||||
"sha256:7964b17923c1bfa519afe273335023e0800c64bdca854008e75f2b148614d3f2",
|
||||
"sha256:7c7913d3cf7017bd693177ca0a4262d51587378d9c4ae38d13be3655386f0c27",
|
||||
"sha256:8076650a08cec080f6726860c769320c27eb4379cfd22e2f5732787dec119bfe",
|
||||
"sha256:811f559e0e5fca85d26b834e02f2a767aa7765e6b1529d4b2f9d4e9015885b4b",
|
||||
"sha256:824bd92e53f8e32dfa4bf38643246d1a500b13461ade361d342a8fcc3ddb6905",
|
||||
"sha256:833e06b01ff8f5a9f5b52156a23e9930402d964c96130f6d0bd5297e5dec95dc",
|
||||
"sha256:8360759897d50e4f7ec8be51f788119bd43a61b1fe9c68a508a7ba495144859a",
|
||||
"sha256:8627fc0c9806d6dac2fb422d9cd650b0d225f498601381d334685b9f071b793c",
|
||||
"sha256:86dd5b8031d690afc90430997187a4fc5871bc6b81d73055354b8eb48b3e6342",
|
||||
"sha256:8c84c3df9b921439189d0be6ad4f4212085155813475a58fbc5fb3f1d5e8a001",
|
||||
"sha256:8c89219a672d0a15ab70f8a6f41bc8355296ec26becef89a127c1a32bb2e6345",
|
||||
"sha256:8f267edd51db6903c67b2a2b0f780bb0e52d2e92ec569ddd241486eeff347283",
|
||||
"sha256:90178b8c6f75b43612dadf50ff0df08a560e220424ce33cf6d2514d7ab1803a7",
|
||||
"sha256:90b35553c318b49d5ffdaf3d25b6f0117fd5bbfc3be5576fc41ca506ca0e9b8e",
|
||||
"sha256:9101d48f9532ceb6b1d6a5f7d3a2dd5c853015850c65a47045c70f5f2f9ff88f",
|
||||
"sha256:946e97712014784c3257e4ca45cf5071ffdbbebe83977d429e8f7329d0e2387f",
|
||||
"sha256:9511420cf727eb6e603dc6f3c122da1a16af38abc92272a715ce68c47b19b140",
|
||||
"sha256:958b75f26f8abbcb9bc47a8a546a0449ba565d6aac819e5bb80417b93e5777fa",
|
||||
"sha256:99ea63932e86b08e36d6246ff8f663728a5baefa7e9a0e2f682864fe13297514",
|
||||
"sha256:9aa5cf7a6a8597968ff6f4e7488d5518bba911344b32b7948012a41ca3ae7e41",
|
||||
"sha256:9c8f580590822df5675b9bc04b9df534be23a4917f709f9483fa554fd2e0a4df",
|
||||
"sha256:9ce64e247af33fa348694dbf7f4943a60040b5cc04df813649cc8b54c7f54061",
|
||||
"sha256:9d6fe373572b20adde2d6a58f8dc900b0cb4eec625b05ca1adbf053772723c78",
|
||||
"sha256:a0c87ffc5bf3669b0dfa91752653c41c9c38e1fd5b95aeb4c7ee40208c953fcd",
|
||||
"sha256:a1adc8cd484de52b6b11a0e59e087cd3ae593ce4c822c18d4095d16e06e49453",
|
||||
"sha256:a29ad824bf4b735cb119e2c79a4b821ad462aeb4495e80ff186f1a8e48362082",
|
||||
"sha256:ab52dd26d24061d67f485f3400cc7d3d5696f0246294a372ef09aa8ef31a44c4",
|
||||
"sha256:ac5d80cd43a9a995a501b4e3b38802628b35065e896f79d33430989e2e3f0870",
|
||||
"sha256:af6a09c296aa2d68b25eb154079abd5a58da883db179e9df0fc9215c405be6be",
|
||||
"sha256:b0bca424ee4d80a4880da332e56d2863e8d75305842c10aa6e94eb975bcad4fc",
|
||||
"sha256:b521c2d73f6fa1c461a68c5d220836d0fea9261d5f934833aaffde5114aecffb",
|
||||
"sha256:b77a03aba84bf2d2c8f2d5a81af5957da42324d9f701d584236dc735b6a191f8",
|
||||
"sha256:b81664adf97f54cb174472f5511075bfb5e8fb13151e9c1592a09b45d544dab0",
|
||||
"sha256:b9f2247b76e2e8c88f81fb850adb211d9b322f498ae7e5797f7629954f5b9767",
|
||||
"sha256:bf7ead8b947a14c785d04943ff4743db90b0c40a4cb27e6bef4c3650800a927d",
|
||||
"sha256:c001b7ac2d9cf1a73899cf857d3d66919deca677df26df905852039c46aa30a6",
|
||||
"sha256:c00b2ea9aab5b2c623b1901a4c04043fb847c8bd64a2f52518488434eb44c4e6",
|
||||
"sha256:c17eae957d61fea05d3f2333a95dd79dc4733f3eadf44862cd6d586daae31ea3",
|
||||
"sha256:c17fd3a63b31a21a979962bd3ab0f96d22dcdb79dc5149efc2cf66a16ae0bb59",
|
||||
"sha256:cb388586c9b4d338f9585885a6f4bd2736d4a7a7eb4b63746587cb8d04f7d156",
|
||||
"sha256:cbf063667ef89b0d8b8bd1fcaaa4dcc8c65c17048eb14fb1fa9dbe9cb5197c81",
|
||||
"sha256:d030b96f6ccfec0812e2fc1b02ab72d56a408ec215f496a7a25cde31160a88b4",
|
||||
"sha256:d2c411b7d3784109dfc33f5f7cdf331d3373b8349a4ad608ee482f1a04c30efe",
|
||||
"sha256:d2c8b7da269eb877cc2361d868fdcb63bfe7b5821c5b3ea2640be3f4b047b4bb",
|
||||
"sha256:d3f5cec4f8d27284f559a0d7c4a4bdfbae74d3b69d09c3f3b53989a730833ad8",
|
||||
"sha256:d40dbc3609f1471ca3c189815ab4596adae75d8ee0da01412b2e3d0f6e94ab46",
|
||||
"sha256:d57b3b92bfa453cba737716680292afb313ec92ada6c139847e005f5ac1ad08c",
|
||||
"sha256:da225a602cb4a97900e416059bc77d7b0bb8ac5cb6cb3cc734fd01c636387d2b",
|
||||
"sha256:dc448e4871fc4df22dd04db4a7b34829e5c3404003b9b1709b6b496d340db9c7",
|
||||
"sha256:dc6407e899fc3148d796fc4c3b0cec78153f034c5ff9baa6ae9c91d7ea05fb45",
|
||||
"sha256:dd0ba0cc46b9a7d5cee4c4a9733dce2f0aa21caf04fe18d18d2025a4211adc18",
|
||||
"sha256:dea204d3c6ec17fc3084c1db11bcad1347f707b7f5c08664e116a9c75ca134e9",
|
||||
"sha256:e362fc7a72fd00f641b3d6ed91076174cae36f49183afe8b4b4b77a2b5a116b0",
|
||||
"sha256:e44be933a60b27ef0378a2fdc111ae4ac53a090169db9f97219910cac51ff885",
|
||||
"sha256:e61b7552c953e58cf2d82b95843ca410eef18af2a5380f3ff058d21eaf902eda",
|
||||
"sha256:e75c4a1f00f46057f2fc98d717b2eabba09582422fe608158beed2ef0a5642da",
|
||||
"sha256:e95f13d615f91da5a5ee5a782d9041c58be051661843416f2df9453f57008d40",
|
||||
"sha256:e9b18889a809d8f190e09dd6ee513983e1cdc04c3f23395d237ccf699dce5eaf",
|
||||
"sha256:ea48f168274d60f900f847dd5fff9bd9d4e4f8af5a84149037c2b5fe1712fa0b",
|
||||
"sha256:ed6f9b158c11e7bcf9b0b6788003aed5046a0759e7b25e224d9551a01c779ee7",
|
||||
"sha256:eeda85d034a2649b7e4dbd7067411e9c55c1fc65fafb9feb973d810b103e36a0",
|
||||
"sha256:f1767c325ef4983f52a9d62590f09ea998c06d8d4aa9f13b9eeabaac3536381e",
|
||||
"sha256:f26f3197779fe5a90a54505334d34ceb948cec6378caea49cd9153b3bbe57566",
|
||||
"sha256:f46e7fe734b57f3783a324bf3a7053df54299653e646d86558a4b2576cb47208",
|
||||
"sha256:f4e2fc0f6a573979462786edbf233fc9e1b644b4e790e8c29796f96bbe45353a",
|
||||
"sha256:f51322a55687f1ac075b897d409d0314a81f1ec55ebae96eeca40c9e8ad4a1c1",
|
||||
"sha256:f5f44d71486949237679a8052cda171244d0be9279776c1d3d276861950dd608",
|
||||
"sha256:f62738cc16a387aa2f0dc6e93e0b0f48d5b084db249f632a0e3048d5ace783e6",
|
||||
"sha256:f7cee295219988b50b543791570b013e3f3325867f9650f6233b48cb00b020c2",
|
||||
"sha256:f7eb851d62a3166b8d1da5d5740509e215fa5b986467bf135a5a2d197bf16345",
|
||||
"sha256:f937ef83e5666b6266236f59b1f38abe64851fb20e7d8d13033c5168d35ef39d",
|
||||
"sha256:fd3ed1f7d2d33856252863d5fa976c41013fac4eb0898bf7c3f5341f7ae73e06"
|
||||
],
|
||||
"version": "==3.0.0"
|
||||
"version": "==3.3.1"
|
||||
},
|
||||
"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": [
|
||||
@@ -249,51 +246,58 @@
|
||||
},
|
||||
"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:04abd71114848aa25edb28e225ab5f268096f44cf0127f3d36975bdf1bdf3390",
|
||||
"sha256:0529b1d5a0105dd3731fa65680b45ce49da4d8115ea76e9da77a875396727b41",
|
||||
"sha256:1bc312dfb7a6e5d66082c87c34c8a62176e684b6fe3d90fcfe1568de675e6688",
|
||||
"sha256:268e4e9b177c76d569e8a145a6939eca9a5fec658c932348598818acf31ae9a5",
|
||||
"sha256:29ecec49f3ba3f3849362854b7253a9f59799e3763b0c9d0826259a88efa02f1",
|
||||
"sha256:2bf7bf75f7df9715f810d1b038870309342bff3069c5bd8c6b96128cb158668d",
|
||||
"sha256:3b721b8b4d948b218c88cb8c45a01793483821e709afe5f622861fc6182b20a7",
|
||||
"sha256:3c00b6b757b32ce0f62c574b78b939afab9eecaf597c4d624caca4f9e71e7843",
|
||||
"sha256:3dc62975e31617badc19a906481deacdeb80b4bb454394b4098e3f2525a488c5",
|
||||
"sha256:4973da6ca3db4405c54cd0b26d328be54c7747e89e284fcff166132eb7bccc9c",
|
||||
"sha256:4e389622b6927d8133f314949a9812972711a111d577a5d1f4bee5e58736b80a",
|
||||
"sha256:51e4de3af4ec3899d6d178a8c005226491c27c4ba84101bfb59c901e10ca9f79",
|
||||
"sha256:5f6f90b72d8ccadb9c6e311c775c8305381db88374c65fa1a68250aa8a9cb3a6",
|
||||
"sha256:6210c05941994290f3f7f175a4a57dbbb2afd9273657614c506d5976db061181",
|
||||
"sha256:6f101b1f780f7fc613d040ca4bdf835c6ef3b00e9bd7125a4255ec574c7916e4",
|
||||
"sha256:7bdcd82189759aba3816d1f729ce42ffded1ac304c151d0a8e89b9996ab863d5",
|
||||
"sha256:7ca25849404be2f8e4b3c59483d9d3c51298a22c1c61a0e84415104dacaf5562",
|
||||
"sha256:81276f0ea79a208d961c433a947029e1a15948966658cf6710bbabb60fcc2639",
|
||||
"sha256:8cadc6e3b5a1f144a039ea08a0bdb03a2a92e19c46be3285123d32029f40a922",
|
||||
"sha256:8e0ddd63e6bf1161800592c71ac794d3fb8001f2caebe0966e77c5234fa9efc3",
|
||||
"sha256:909c97ab43a9c0c0b0ada7a1281430e4e5ec0458e6d9244c0e821bbf152f061d",
|
||||
"sha256:96e7a5e9d6e71f9f4fca8eebfd603f8e86c5225bb18eb621b2c1e50b290a9471",
|
||||
"sha256:9a1e657c0f4ea2a23304ee3f964db058c9e9e635cc7019c4aa21c330755ef6fd",
|
||||
"sha256:9eb9d22b0a5d8fd9925a7764a054dca914000607dff201a24c791ff5c799e1fa",
|
||||
"sha256:af4ff3e388f2fa7bff9f7f2b31b87d5651c45731d3e8cfa0944be43dff5cfbdb",
|
||||
"sha256:b042d2a275c8cee83a4b7ae30c45a15e6a4baa65a179a0ec2d78ebb90e4f6699",
|
||||
"sha256:bc821e161ae88bfe8088d11bb39caf2916562e0a2dc7b6d56714a48b784ef0bb",
|
||||
"sha256:c505d61b6176aaf982c5717ce04e87da5abc9a36a5b39ac03905c4aafe8de7aa",
|
||||
"sha256:c63454aa261a0cf0c5b4718349629793e9e634993538db841165b3df74f37ec0",
|
||||
"sha256:c7362add18b416b69d58c910caa217f980c5ef39b23a38a0880dfd87bdf8cd23",
|
||||
"sha256:d03806036b4f89e3b13b6218fefea8d5312e450935b1a2d55f0524e2ed7c59d9",
|
||||
"sha256:d1b3031093a366ac767b3feb8bcddb596671b3aaff82d4050f984da0c248b615",
|
||||
"sha256:d1c3572526997b36f245a96a2b1713bf79ce99b271bbcf084beb6b9b075f29ea",
|
||||
"sha256:efcfe97d1b3c79e486554efddeb8f6f53a4cdd4cf6086642784fa31fc384e1d7",
|
||||
"sha256:f514ef4cd14bb6fb484b4a60203e912cfcb64f2ab139e88c2274511514bf7308"
|
||||
],
|
||||
"markers": "python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1'",
|
||||
"version": "==44.0.0"
|
||||
"version": "==44.0.2"
|
||||
},
|
||||
"ecdsa": {
|
||||
"hashes": [
|
||||
"sha256:2cea9b88407fdac7bbeca0833b189e4c9c53f2ef1e1eaa29f6224dbc809b707a",
|
||||
"sha256:60eaad1199659900dd0af521ed462b793bbdf867432b3948e87416ae4caf6bf8"
|
||||
"sha256:30638e27cf77b7e15c4c4cc1973720149e1033827cfd00661ca5c8cc0cdb24c3",
|
||||
"sha256:478cba7b62555866fcb3bb3fe985e06decbdb68ef55713c4e5ab98c57d508e61"
|
||||
],
|
||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==0.19.0"
|
||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
|
||||
"version": "==0.19.1"
|
||||
},
|
||||
"esptool": {
|
||||
"hashes": [
|
||||
"sha256:dc4ef26b659e1a8dcb019147c0ea6d94980b34de99fbe09121c7941c8b254531"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==4.8.1"
|
||||
},
|
||||
"intelhex": {
|
||||
@@ -309,7 +313,6 @@
|
||||
"sha256:d3ae3d0a0ae7713c537be2b6afadd11c7cde5f1750ea1260f6667bb80071b15b"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_version >= '3.4'",
|
||||
"version": "==1.24.1"
|
||||
},
|
||||
"pycparser": {
|
||||
|
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)
|
||||
|
37
src/main.py
37
src/main.py
@@ -1,24 +1,24 @@
|
||||
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_color(0,(tuple(int(settings["color1"][i:i+2], 16) for i in color_order)))
|
||||
patterns.set_color(1,(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"]))
|
||||
|
||||
@@ -30,26 +30,17 @@ 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())
|
||||
|
||||
first = True
|
||||
asyncio.create_task(p2p(settings, patterns))
|
||||
|
||||
while True:
|
||||
|
||||
#print(time.localtime())
|
||||
|
||||
# gc.collect()
|
||||
for i in range(60):
|
||||
wdt.feed()
|
||||
await asyncio.sleep_ms(500)
|
||||
gc.collect()
|
||||
for i in range(20):
|
||||
wdt.feed()
|
||||
await asyncio.sleep_ms(1000)
|
||||
|
||||
# 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:
|
||||
await settings.set_settings(data.get("settings", {}), patterns, data.get("save", False))
|
||||
print("should not print")
|
407
src/patterns.py
407
src/patterns.py
@@ -1,4 +1,5 @@
|
||||
from machine import Pin
|
||||
import asyncio
|
||||
from machine import Pin, WDT
|
||||
from neopixel import NeoPixel
|
||||
import utime
|
||||
import random
|
||||
@@ -7,38 +8,25 @@ class Patterns:
|
||||
def __init__(self, pin, num_leds, color1=(0,0,0), color2=(0,0,0), brightness=127, selected="rainbow_cycle", delay=100):
|
||||
self.n = NeoPixel(Pin(pin, Pin.OUT), num_leds)
|
||||
self.num_leds = num_leds
|
||||
self.pattern_step = 0
|
||||
self.last_update = utime.ticks_ms()
|
||||
self.delay = delay
|
||||
self.brightness = brightness
|
||||
self.patterns = {
|
||||
"off": self.off,
|
||||
"on" : self.on,
|
||||
"color_wipe": self.color_wipe_step,
|
||||
"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,
|
||||
"external": None
|
||||
"blink": self.blink,
|
||||
"rainbow": self.rainbow,
|
||||
"theater chase": self.theater_chase,
|
||||
"flicker": self.flicker # Added flicker pattern
|
||||
|
||||
}
|
||||
self.selected = selected
|
||||
self.color1 = color1
|
||||
self.color2 = color2
|
||||
self.transition_duration = 50 # Duration of color transition in milliseconds
|
||||
self.transition_step = 0
|
||||
|
||||
def sync(self):
|
||||
self.pattern_step=0
|
||||
self.last_update = utime.ticks_ms()
|
||||
# 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.task = None
|
||||
self.pattern_step = 0
|
||||
|
||||
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
|
||||
@@ -49,243 +37,186 @@ class Patterns:
|
||||
|
||||
def set_brightness(self, brightness):
|
||||
self.brightness = brightness
|
||||
|
||||
def set_color1(self, color):
|
||||
print(color)
|
||||
self.color1 = self.apply_brightness(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)
|
||||
|
||||
def select(self, pattern):
|
||||
if pattern in self.patterns:
|
||||
self.selected = pattern
|
||||
|
||||
def set_colors(self, colors):
|
||||
self.colors = colors
|
||||
|
||||
def set_color(self, num, color):
|
||||
# Changed: More robust index check
|
||||
if 0 <= num < len(self.colors):
|
||||
self.colors[num] = color
|
||||
return True
|
||||
elif num == len(self.colors): # Allow setting a new color at the end
|
||||
self.colors.append(color)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
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]
|
||||
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)
|
||||
|
||||
async def select(self, pattern, reset = True):
|
||||
if pattern not in self.patterns:
|
||||
return False
|
||||
self.selected = pattern
|
||||
if self.task is not None:
|
||||
self.task.cancel()
|
||||
if reset: self.pattern_step = 0
|
||||
print(pattern)
|
||||
self.task = asyncio.create_task(self.patterns[pattern]())
|
||||
return True
|
||||
|
||||
def set(self, i, color):
|
||||
self.n[i] = color
|
||||
|
||||
def write(self):
|
||||
self.n.write()
|
||||
|
||||
def fill(self):
|
||||
for i in range(self.num_leds):
|
||||
self.n[i] = self.color1
|
||||
|
||||
def fill(self, color):
|
||||
self.n.fill(color)
|
||||
self.n.write()
|
||||
|
||||
def off(self):
|
||||
color = self.color1
|
||||
self.color1 = (0,0,0)
|
||||
self.fill()
|
||||
self.color1 = color
|
||||
|
||||
def on(self):
|
||||
color = self.color1
|
||||
self.color1 = self.apply_brightness(self.color1)
|
||||
self.fill()
|
||||
self.color1 = color
|
||||
|
||||
|
||||
def color_wipe_step(self):
|
||||
color = self.apply_brightness(self.color1)
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay:
|
||||
if self.pattern_step < self.num_leds:
|
||||
async def off(self):
|
||||
self.fill((0, 0, 0))
|
||||
|
||||
async def on(self):
|
||||
self.fill(self.apply_brightness(self.colors[0]))
|
||||
|
||||
def sync(self):
|
||||
self.pattern_step = 0
|
||||
|
||||
async def rainbow(self):
|
||||
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)
|
||||
last_update = utime.ticks_ms()
|
||||
while True:
|
||||
if utime.ticks_diff(utime.ticks_ms(), last_update) >= self.delay:
|
||||
for i in range(self.num_leds):
|
||||
self.n[i] = (0, 0, 0)
|
||||
self.n[self.pattern_step] = self.apply_brightness(color)
|
||||
rc_index = (i * 256 // self.num_leds) + self.pattern_step
|
||||
self.n[i] = self.apply_brightness(wheel(rc_index & 255))
|
||||
self.n.write()
|
||||
self.pattern_step += 1
|
||||
else:
|
||||
self.pattern_step = 0
|
||||
self.last_update = current_time
|
||||
self.pattern_step = (self.pattern_step + 1) % 256
|
||||
last_update += self.delay
|
||||
await asyncio.sleep(0)
|
||||
|
||||
def rainbow_cycle_step(self):
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay/5:
|
||||
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)
|
||||
|
||||
for i in range(self.num_leds):
|
||||
rc_index = (i * 256 // self.num_leds) + self.pattern_step
|
||||
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 theater_chase_step(self):
|
||||
current_time = utime.ticks_ms()
|
||||
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)
|
||||
else:
|
||||
self.n[i] = (0, 0, 0)
|
||||
self.n.write()
|
||||
self.pattern_step = (self.pattern_step + 1) % 3
|
||||
self.last_update = current_time
|
||||
|
||||
def blink_step(self):
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay:
|
||||
if self.pattern_step % 2 == 0:
|
||||
async def theater_chase(self):
|
||||
last_update = utime.ticks_ms()
|
||||
while True:
|
||||
if utime.ticks_diff(utime.ticks_ms(), last_update) >= self.delay:
|
||||
for i in range(self.num_leds):
|
||||
self.n[i] = self.apply_brightness(self.color1)
|
||||
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)
|
||||
if (i + self.pattern_step) % 3 == 0:
|
||||
self.n[i] = self.apply_brightness(self.colors[0])
|
||||
else:
|
||||
self.n[i] = (0, 0, 0)
|
||||
self.n.write()
|
||||
self.pattern_step += 1
|
||||
else:
|
||||
self.pattern_step = 0
|
||||
self.last_update = current_time
|
||||
self.pattern_step = (self.pattern_step + 1) % 3
|
||||
last_update += self.delay
|
||||
await asyncio.sleep(0)
|
||||
|
||||
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)
|
||||
async def blink(self):
|
||||
last_update = utime.ticks_ms()
|
||||
self.pattern_step = 0
|
||||
while True:
|
||||
if utime.ticks_diff(utime.ticks_ms(), last_update) >= self.delay:
|
||||
if self.pattern_step:
|
||||
self.off()
|
||||
self.pattern_step = 0
|
||||
else:
|
||||
pos -= 170
|
||||
return (0, pos * 3, 255 - pos * 3)
|
||||
self.on()
|
||||
self.pattern_step = 1
|
||||
last_update += self.delay
|
||||
await asyncio.sleep(0)
|
||||
|
||||
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
|
||||
async def flicker(self):
|
||||
last_update = utime.ticks_ms()
|
||||
while True:
|
||||
if utime.ticks_diff(utime.ticks_ms(), last_update) >= self.delay:
|
||||
# Calculate a single flicker amount for all LEDs
|
||||
flicker_amount = random.randint(int(-self.brightness // 1.5), int(self.brightness // 1.5))
|
||||
flicker_brightness = max(0, min(255, self.brightness + flicker_amount))
|
||||
self.fill(self.apply_brightness(self.colors[0], brightness_override=flicker_brightness))
|
||||
last_update += self.delay
|
||||
await asyncio.sleep(0)
|
||||
|
||||
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
|
||||
async def color_transition(self):
|
||||
if len(self.colors) < 2:
|
||||
# If there's only one color or no colors, just display that color (or off)
|
||||
self.fill(self.apply_brightness(self.colors[0]))
|
||||
return
|
||||
|
||||
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.pattern_step = (self.pattern_step + 1) % 2
|
||||
self.last_update = current_time
|
||||
last_transition_start_time = utime.ticks_ms()
|
||||
current_color_index = 0
|
||||
transition_duration_ms = self.delay # Use self.delay as the transition 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()
|
||||
while True:
|
||||
color_from = self.colors[current_color_index]
|
||||
color_to = self.colors[(current_color_index + 1) % len(self.colors)]
|
||||
|
||||
self.pattern_step += self.delay
|
||||
if self.pattern_step > self.transition_duration:
|
||||
self.pattern_step = 0
|
||||
start_time = utime.ticks_ms()
|
||||
elapsed_time = 0
|
||||
|
||||
self.last_update = current_time
|
||||
while elapsed_time < transition_duration_ms:
|
||||
# Calculate the interpolation factor (0.0 to 1.0)
|
||||
# Maximize to avoid division by zero if delay is 0, though a meaningful delay is expected
|
||||
t = min(1.0, elapsed_time / max(1, transition_duration_ms))
|
||||
|
||||
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):
|
||||
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.n.write()
|
||||
self.pattern_step += self.delay
|
||||
if self.pattern_step > self.transition_duration:
|
||||
self.pattern_step = 0
|
||||
# Interpolate each color component
|
||||
interpolated_color = (
|
||||
int(color_from[0] + (color_to[0] - color_from[0]) * t),
|
||||
int(color_from[1] + (color_to[1] - color_from[1]) * t),
|
||||
int(color_from[2] + (color_to[2] - color_from[2]) * t)
|
||||
)
|
||||
|
||||
self.last_update = current_time
|
||||
self.fill(self.apply_brightness(interpolated_color))
|
||||
await asyncio.sleep(0) # Update smoothly
|
||||
elapsed_time = utime.ticks_diff(utime.ticks_ms(), start_time)
|
||||
|
||||
# Ensure the final color is set precisely after interpolation loop
|
||||
self.fill(self.apply_brightness(color_to))
|
||||
|
||||
current_color_index = (current_color_index + 1) % len(self.colors)
|
||||
await asyncio.sleep(0) # Yield control
|
||||
|
||||
async def main():
|
||||
w = WDT(timeout = 10000)
|
||||
p = Patterns(num_leds=10, pin=10, color1=(16,16,0))
|
||||
# p.set_delay(100)
|
||||
# await p.select("blink")
|
||||
# await asyncio.sleep(2)
|
||||
# p.set_delay(10)
|
||||
# await p.select("rainbow")
|
||||
# await asyncio.sleep(2)
|
||||
# p.set_delay(100)
|
||||
# await p.select("theater chase")
|
||||
# await asyncio.sleep(2)
|
||||
# p.set_colors([(255, 100, 0)]) # Set a base color for flicker (e.g., orange for a candle effect)
|
||||
# p.set_brightness(200) # Set a brighter base for flicker to allow for dimming
|
||||
# p.set_delay(100) # Faster updates for a more convincing flicker
|
||||
# await p.select("flicker")
|
||||
# await asyncio.sleep(2)
|
||||
w.feed()
|
||||
# Test the new color transition pattern
|
||||
print("Starting color transition...")
|
||||
p.set_colors([(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0)]) # Red, Green, Blue, Yellow
|
||||
p.set_delay(1000) # 1 second transition between colors
|
||||
p.set_brightness(150)
|
||||
await p.select("color transition")
|
||||
await asyncio.sleep(10) # Let it run for 10 seconds
|
||||
|
||||
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))
|
||||
asyncio.run(main())
|
||||
|
@@ -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()
|
||||
|
||||
async 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_color(0,(tuple(int(value[i:i+2], 16) for i in self.color_order))) # Convert hex to RGB
|
||||
elif key == "color2":
|
||||
patterns.set_color(1,(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 await 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 = await 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