Compare commits

...

14 Commits

Author SHA1 Message Date
d00d21e2b6 Split up main.js 2025-07-08 18:24:54 +12:00
deca1b6c37 Add basic draggable item that saves to the server 2025-07-08 17:39:09 +12:00
5c35e68ab2 Rename to styles.css 2025-07-07 18:20:11 +12:00
8b6bbdeb56 Update wifi.py 2025-05-18 21:31:03 +12:00
09bc09cca3 Update web.py 2025-05-18 21:31:00 +12:00
e57feda131 Delete index_html.py 2025-05-18 21:30:57 +12:00
3242aa464b Update index.html 2025-05-18 21:30:50 +12:00
72b7ba39ef Update main.js 2025-05-18 21:30:47 +12:00
c2a0cfaef4 Update main.css 2025-05-18 21:30:44 +12:00
4c3337a232 Update settings.py 2025-05-18 21:30:40 +12:00
825ae1f637 Update main.py 2025-05-18 21:30:37 +12:00
14a70cb024 Update boot.py 2025-05-18 21:30:34 +12:00
425511d41f Create Pipfile.lock 2025-05-18 21:30:30 +12:00
3e5239f3c6 Create patterns.py 2025-05-18 21:30:24 +12:00
15 changed files with 1137 additions and 480 deletions

414
Pipfile.lock generated Normal file
View File

@@ -0,0 +1,414 @@
{
"_meta": {
"hash": {
"sha256": "8b14bb293b7e7117ffc89c2bc92d7aa2290e8f68be7fc0f073f2b3f7f959ef71"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.12"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"argcomplete": {
"hashes": [
"sha256:65b3133a29ad53fb42c48cf5114752c7ab66c1c38544fdf6460f450c09b42591",
"sha256:d0519b1bc867f5f4f4713c41ad0aba73a4a5f007449716b16f385f2166dc6adf"
],
"markers": "sys_platform != 'win32'",
"version": "==3.6.2"
},
"bitarray": {
"hashes": [
"sha256:013ba795deb6c54fdb0e70103fc142f97746074d2f67b4b6a8f67a17f2d03f06",
"sha256:01df531279959c95c0eb1eccd3e6121cb241ddcb821594f3eb07a94b086f71a0",
"sha256:0330f470bdb76825d760215e01f8d60ce09d4ac84434b364e27236db5657d323",
"sha256:05db62a7867702ddc7f4c58ed3804d5aa9cc0cf5ce652f98b30281a2d1174bda",
"sha256:0626cfd86070cc71bf089e9c62c27c03ced24d3ebc44ff9b1c6a590991ace74f",
"sha256:07d9fa226a06971ca35c720c99666cb8542f0e4d5cf234583e0822b45e68755b",
"sha256:0a4bb5dd53250e3c70924fd473034cb2e741027938702d9cc319646e53091dc1",
"sha256:0bedc6531388e719d8fa1eb80b1bcf97ccdccedf4a0daa02bc4f81d34a50d309",
"sha256:0dd3b351628fe0edf812d8a7e29d2b44ca8a5599d871fadf5cfa5362dbd10689",
"sha256:0e942e5ac197e31ce6108ab783cbb177f6372289a5a0c9a84dcd8e3ea1129748",
"sha256:0efde6c15876a159733d6d57512fc565581e3bba877ad84508b224758c4bd50f",
"sha256:1037ac94bf04f59e0085b24dc8252d663e6e5024af4de1372bc026efa8d4bd01",
"sha256:10c1dc463de03521b7a350426449daa1606177e2e15f6874a27b1a7330f42a4f",
"sha256:11fcc8e92699a2463055ceab63071ff2179a1f53d1284f4b7b9a405365065efa",
"sha256:122d390230075671420845b6d59d01d9e3e5a16f9ad8791a78ef06396a6ca2d7",
"sha256:12cc15dba08edb6e80c3a7f43cfaebba98dcbb89b120d534e32a42cd57c5f15f",
"sha256:12fb9cdd96d1d1646d4fe67aa21e28d2fcedb431c703d1f37b2221fcb0cfe0c9",
"sha256:15197c8a3ec258401f80bbcc64b942d82dfcf3d9549320147aef900c80bdf77b",
"sha256:15714d2dc4fe6bb3c93ffa88cab026da993c6bc131c191fb3c59f697847a7621",
"sha256:16651c323570bf9ddf43b155aa47d58c0046bd0f98cec3fdd8cfac9a9468da5c",
"sha256:171f2fd9f0d3e3e9f08addcd8393677df7e5e55a294c13b5ac16a961870ee647",
"sha256:1babc8dba17fad7409ca1cfe6ec4b89d175070f20d2c6f97f87d1c257be4aea9",
"sha256:1ed6b53f947ae738258175ecf8f249172a416204f18ce67ba67a3f02dc910703",
"sha256:1feb9bf948d075d7599632c14d3a499e31718502355f2ec96690d09ff7f71b8d",
"sha256:1fec333d4c744b32396a46378ed42b05ffa90aa62ae99ed799c851e6a2134327",
"sha256:20408d8d6eb3ff5ef3c62ece8a785b7cfb1a4be6979ee614eac63d578fe9b303",
"sha256:2232724b1b0822ca56a6649769147104306849d2841bba5cdee746c4748ce34b",
"sha256:268ec3d5744ced25edfcf65e01ce4b72592b0b587d8919bc409288e97e2831a1",
"sha256:27bb390521ba1032b95e31683fa9aed042222fca653760d5101435c2dbf28ede",
"sha256:2bcc21447e4a7f132485905b471164d51030da829563512789a7085817545f3b",
"sha256:2d0b70cf75f82c919fe486af185895a77644ac3621ea8bd5b5a82fd21c03c843",
"sha256:30f8925e9a101843a89e55f527f581b9da34bd97a697c063754be0b681c49694",
"sha256:332a373fe20fdba78968bbeeb1aa01f2d861a30d938bb986e7101246cf371500",
"sha256:336369abb755401278221577f83963d4522a0454de4bbf3cc913d24855d8a47e",
"sha256:33eee090eade2c8303bfc01a9e104fea306d330035b18b5c50a04cb0cb76f08d",
"sha256:340dd788dad07ad004b591925e4b906786aaefb6632ea9d9ac616913f3cafa4e",
"sha256:341104cb87536114dc30728231427a335db4f90ea7e9ab94d8b1a94ff253624f",
"sha256:35c4d724b79a3a0878999dff799d477c7eb771fd96695cf9bc5aec8aa4d956a2",
"sha256:36efcce5a4b18c0920d623c99fa16d2fd1bd2315e666f829d50c1a6fa1d6891a",
"sha256:3b6dede8d214301d2e0133c76cb59c713ca9dfbadd039165dfde181f4aa8f6b7",
"sha256:3d089a0570e2acfabac9dd40ee7bfbc36ec48ff73c9312f3e61ebf31b315d05d",
"sha256:3edbc614128b1f054584924e330f04f083f2e2bbb1bb6df1559a85cca490d408",
"sha256:4292ef2a67ff6a3811e018c7e32c3ce4fb74c2f5c85257c06222895138df86f4",
"sha256:437d983fb4b34874faa2c6a0247be770ec3935b4cedc16f65f8a4cbf8c970f03",
"sha256:4616a3318ce687b0bda0b3f09f9b074f521ebf6f6fd527c1ff96619390a3f3e2",
"sha256:49a41a724693b9f15ac965f548c2f68f6ff7b0ab36a29009d82e99f7d402888b",
"sha256:4aa615307dbe11f8776af6a01a056ac6851ab200ef7a4ab49140bfac2fada5e4",
"sha256:4db7cd3926ba52c5f4d4e76f9813ccc89b8c2cb077fccac0b86289f5db9b3710",
"sha256:4fddc0366431da5b6c0f5527e8e7093680f76706420acdfb460be4a6cfb03197",
"sha256:51126ccfb42d32da59f8eccaba2952e6091be534d45f594ea2b60a9c621734f5",
"sha256:51e27ac27a6c85eb4f970e71134c0dc60b753ec9d18e2aeac5b3a5b31ea0847d",
"sha256:5776328e0630af51e11c6dcf44490ef8c4b4f862e88ca48cb619ef65d20e6b67",
"sha256:5811b9aeacc8c2b62ed0732649600405a7df5bf28eb7b7475f56822f702fc718",
"sha256:5af9d61f383335a52c28cac82b2a06ecf7ad72bb6a7e90711cb7534ce8c5fa07",
"sha256:5c62c2ae324c486f8e8f0482d5a8635e255da5302c44e7a5df83eee7d87e28ec",
"sha256:5cfd6bbd0bdd47335828a8269ce66729dff6125b4f92642f465f9924f22f4fba",
"sha256:5e5fac8a9d1140ae55858f13914574ca63b48f968e424a02d918e46602569c02",
"sha256:64fb1eef44861fa301f393f8b4a6606c6b030db152b8aeaa6e5370c75887c1b6",
"sha256:6799c9002876eae5e88a48220638144d80cc3e754dfae7333482b2b185f9bc31",
"sha256:6d39ea865f22c14f7adf44e645ff71d459b3e9588c58c71ef7b8ce488b90b29c",
"sha256:6d4564cc36b12e8ba5d40c8fde9978012dfe912d038343c12a01b88df8ca90a1",
"sha256:6e7274cdfe405c4e70a585b997d3a8c001425c03fa37d09a8e5460828a3d8bd6",
"sha256:6e812aabec2438a50c58c15755db8a9f7679b604a9d3992903f9a29a9da37356",
"sha256:70a01907ceebadbd6082b971d57d80e5af97667a8a45938a46ae23df42589c9b",
"sha256:7201d4574f70a0f563a27e12028e39231bde277f8a2768a2c530c4f82f95b3b8",
"sha256:722c105dd4229b91d17804a0855e8f27519ceee99d8fd4db80bf09b507d7fb60",
"sha256:75860edbfca1550b66105e59ec06a6437935283ee9849d0b2a302f73e1260dad",
"sha256:75df7335ed7324a1ee9002d747c36a37de42b6469601ac39fef00c6bd80a4cb4",
"sha256:77fd3e9c576f3952870b527c3a42795108946862ec11a3b17a723939dae76a12",
"sha256:788947fd11fad912ad17b1dee810e142c63b509a4a7b0211a44549a94baba593",
"sha256:78a8dd296c1ee30f9e3d0fe48d0168d89f99133d797f7fc2b1993bab1b23c21f",
"sha256:7d22ed65d138ddaf63c743a68869e78eac6a7b7632ab97ccfbf0fd96ebc43001",
"sha256:823decea26d8be2ec46000583114d050d02033f99e54e3285c0a80f31e3d7784",
"sha256:83e008a8d7c115926717d826cbd3bc5e35816c63feff43da9456d09df9842743",
"sha256:8733f59c5f07f043d7e9936201fd0674e591244520c8725f0061a8f7dcd71cc1",
"sha256:87e7a438f6a806a3f508f7d9165e3b6162bfbe0351ce1b4a954f9fb76155ea9d",
"sha256:8ab8dc6c2e55ad9bc918672f258ac3a1251fc6621dbe4e3edfc4617b91ad6eb9",
"sha256:8f02850724d3e6c57265246329eeb71893a4a6884521b7f18fc5d9ea467300fe",
"sha256:8f2c1c3d1d0109b993791755f18d4b495f02744118f8f683eed982b9c8ed8687",
"sha256:8f9a4d5595c69b65d4198d952c820e48563d66d3e17aaa46645de1196f5ff12b",
"sha256:9007e6b0a9580eaf2826b2019b7d799ea94249fd167ddd3fde1b6b39f5bff390",
"sha256:93b88fa7bd0f958d7172862dcbebbe7c96eff90f989c6ddd28ec6e28bbe3f768",
"sha256:957524b4f2185bde3e1e0408320ded62de8fb2c4646d3bc74b4c8365cbf9eb50",
"sha256:95e3cb8e64672a977b3b0cc9c0f92b6d398b4aa89c96e84a92688efd312bef2e",
"sha256:95efab4a2fc0f9329332e62e22c505ae8d355991c1c58d4d37d05ae4a6e65b01",
"sha256:99b35f36d70d033d6548a3e90c57fccde197190606de9466869706454993bbc5",
"sha256:9a57105bd2100c2a98cee8fca7cab26a1c0c1f0926b0ae78bc9cc9715c2d83e9",
"sha256:9be0b089fd1cb7d5c88ff0277585df141471ff7ee8ed0bf9b386f23d5fde5573",
"sha256:a2115dce1756537acd870ecdb9ec96ff3ace1f4de470ffc9112c0214fae6aef4",
"sha256:a27456e66fae5726b2b1b9bc3ee0e2f1235bf8a353dc216d2651ad0652596657",
"sha256:a2be3bf9a5230fe6c3c99eab4a7a015f137da4b795e8d7e9f641171ba65398fc",
"sha256:a7eaed4731bd84504176ba5e0af3eba7a6e66afe208d5efb6a8779b66ecd51aa",
"sha256:ae10e24915c7d84f5edb39d5385455b961c66e90a40b786cfcfba59f8399999e",
"sha256:b0f896a433075ab422266ace595f12c49c417ec8bacfa85ae453b45a8694de14",
"sha256:b1f0fa69cb879924391bd7751cc8135602d03b6cdac6dbc830ab60164d70e5a0",
"sha256:b238e48844645ac397cfc67f5c8df86d640a9b33063c82ca2393a39e48b01c15",
"sha256:b2c3541aab6ac40d9090cb52788541bcdc06524d2cdba18f9cc9cbd1edafd093",
"sha256:b38516170daa962e342d54b1677d81c32826f9e94c21856e879b46b6e2008293",
"sha256:bb04ec25c55614d60bbe1591a59ff29149030010ee3e1262c16a43460b193cfd",
"sha256:bb1dcea97241397517cb52dc2646648d33a291534d8b1a8f7039d5583ef6e5f5",
"sha256:bbe0786261a3d7c9214a8c348edc64a75c70ca4eab3164b1ec97aa10c0f0855d",
"sha256:bcfd19c1b3c56dfde1b524e54b0f1d73a439c488dbeb40ff677b9e0a339e2356",
"sha256:c217dc3559b21bcfc58ea37af11f7a25e8b1f71d855992bf3453b9c1ae6c02a0",
"sha256:c233e308fd055fff62679e6a72744cf3b51621a6c18930bbcbe78649bc97f481",
"sha256:c2984abfc4e6281e703675280edbcf7618fa6983367d1fb4822b41917e2c3490",
"sha256:c7576226ee79957e8a4ff64addf2929cbbf2bf749ec622f325adc615d8470b14",
"sha256:c85f235dc643857e2c8e0a93e91f1099dc56db2b4bebd160c08cae8d5ddaec21",
"sha256:c991ba157475c196bdf9c2368d5159208dbdf23d715de8e8d94b8c1cb739bddb",
"sha256:cf924be7b97cc5bec88bf63c09732aa5c90bd00f3152cffebed259a49df351b0",
"sha256:d2c567ce44f9d821776682ece59237b5761443121137afa656b9f586157176af",
"sha256:d3c0db664bffeb4bb80b228ed31773ccb701da11f266f9d8a56732e083e2cab0",
"sha256:d4755823c68ff36b3defb2079258279fb0fb3a2d19dbd1401a28672a10f26ae0",
"sha256:d47810266e7c5b74fe61e983cf7e6e3937120473cba6f6e1ecdc411e6f27b639",
"sha256:d5349869fa33bd28f362f8702cc1bb1a4ec1c7cde7183cdc41933eb794c3d651",
"sha256:d5c10255889045479b86405dd040c58e77ccf4f63a0e6e686d341b5fd8fa32c2",
"sha256:d62db2fbf0a923ecbf5b71babc9deabd5ccea74d275bf74a5e37c050238d8f6a",
"sha256:d6895389eeebf6836cfad1b301bae9e5386e3b94a21076aaf0c2dab0524af6d1",
"sha256:d9ce8cb9161288288859032227039197f4ce6cbf1ff5f022b060216e49f8b591",
"sha256:de59a5a4a54fbdb00c716a8a54935bbe19248c99647c64964b11f2f787a67cec",
"sha256:e11ea9eabf6c886811bdc0bf4ea3350a07c7354c8abc6b6a8bd707eef687bcf5",
"sha256:e1652bf956c8874c790fe78f0dcdc0de04d82ded81373759bfc05f427afd1ff3",
"sha256:e264ad4a850bd1488a754b5e812d09d74fede57bc5ae679a4316ff08aa8edcaa",
"sha256:e4e1437bda368e46871d7fbc8e1de4260c302ff22cfb734e1907f164933c7fdf",
"sha256:e66f6fd558abf563eb68580a3961e47f7c843c2fd80be931027bc484f933b4b7",
"sha256:e8fba33d97fd6b682c9b9107ea1f652485f3149af5aa3b6c8698034007f4adf5",
"sha256:ef3f2dc1a95bec2af77c8685c847d41fc0c64d7329c994b6054c54462f835401",
"sha256:f0e9aa0722b339f971e0f55b3c418d825d1ab7ecd71c2b10115897a3a39352d3",
"sha256:f455c100df47295ca19eb36527462fecbb2710140d92a61228df4cfdd2d7dd81",
"sha256:f4c445ed8e059a3cb6c72e4ff68b947d050791f4007a2b6e68afe12b1b2852ab",
"sha256:f55b48bccd4f5c97401e18e9ad4483ccc4fea2d8feded13eee4a05d6850f90cf",
"sha256:f661ed4bda9a82874cda44829dab0ef604090b8b2f8e9d1759766ffa51f1d6fe",
"sha256:fbbc606b8bf3578356d93db02d071824f66bb7f18e5aa57aa4d74fcd6898d87c",
"sha256:fbcf4b12fa21df99d9a5855aa52e1ec9ab0e42735d9d0f003d0b737c62522e69",
"sha256:fc534fc485fae64ab6a5b21b26815a3dec4e57f0dc373dd3c44242e89437f3ed"
],
"version": "==3.4.0"
},
"bitstring": {
"hashes": [
"sha256:69d1587f0ac18dc7d93fc7e80d5f447161a33e57027e726dc18a0a8bacf1711a",
"sha256:a08bc09d3857216d4c0f412a1611056f1cc2b64fd254fb1e8a0afba7cfa1a95a"
],
"markers": "python_version >= '3.8'",
"version": "==4.3.1"
},
"cffi": {
"hashes": [
"sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8",
"sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2",
"sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1",
"sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15",
"sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36",
"sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824",
"sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8",
"sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36",
"sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17",
"sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf",
"sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc",
"sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3",
"sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed",
"sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702",
"sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1",
"sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8",
"sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903",
"sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6",
"sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d",
"sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b",
"sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e",
"sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be",
"sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c",
"sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683",
"sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9",
"sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c",
"sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8",
"sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1",
"sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4",
"sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655",
"sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67",
"sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595",
"sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0",
"sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65",
"sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41",
"sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6",
"sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401",
"sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6",
"sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3",
"sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16",
"sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93",
"sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e",
"sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4",
"sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964",
"sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c",
"sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576",
"sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0",
"sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3",
"sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662",
"sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3",
"sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff",
"sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5",
"sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd",
"sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f",
"sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5",
"sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14",
"sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d",
"sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9",
"sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7",
"sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382",
"sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a",
"sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e",
"sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a",
"sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4",
"sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99",
"sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87",
"sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"
],
"markers": "platform_python_implementation != 'PyPy'",
"version": "==1.17.1"
},
"cryptography": {
"hashes": [
"sha256:02f55fb4f8b79c1221b0961488eaae21015b69b210e18c386b69de182ebb1259",
"sha256:157f1f3b8d941c2bd8f3ffee0af9b049c9665c39d3da9db2dc338feca5e98a43",
"sha256:192ed30fac1728f7587c6f4613c29c584abdc565d7417c13904708db10206645",
"sha256:21a83f6f35b9cc656d71b5de8d519f566df01e660ac2578805ab245ffd8523f8",
"sha256:25cd194c39fa5a0aa4169125ee27d1172097857b27109a45fadc59653ec06f44",
"sha256:3883076d5c4cc56dbef0b898a74eb6992fdac29a7b9013870b34efe4ddb39a0d",
"sha256:3bb0847e6363c037df8f6ede57d88eaf3410ca2267fb12275370a76f85786a6f",
"sha256:3be3f649d91cb182c3a6bd336de8b61a0a71965bd13d1a04a0e15b39c3d5809d",
"sha256:3f07943aa4d7dad689e3bb1638ddc4944cc5e0921e3c227486daae0e31a05e54",
"sha256:479d92908277bed6e1a1c69b277734a7771c2b78633c224445b5c60a9f4bc1d9",
"sha256:4ffc61e8f3bf5b60346d89cd3d37231019c17a081208dfbbd6e1605ba03fa137",
"sha256:5639c2b16764c6f76eedf722dbad9a0914960d3489c0cc38694ddf9464f1bb2f",
"sha256:58968d331425a6f9eedcee087f77fd3c927c88f55368f43ff7e0a19891f2642c",
"sha256:5d186f32e52e66994dce4f766884bcb9c68b8da62d61d9d215bfe5fb56d21334",
"sha256:5d20cc348cca3a8aa7312f42ab953a56e15323800ca3ab0706b8cd452a3a056c",
"sha256:6866df152b581f9429020320e5eb9794c8780e90f7ccb021940d7f50ee00ae0b",
"sha256:7d5fe7195c27c32a64955740b949070f21cba664604291c298518d2e255931d2",
"sha256:896530bc9107b226f265effa7ef3f21270f18a2026bc09fed1ebd7b66ddf6375",
"sha256:962bc30480a08d133e631e8dfd4783ab71cc9e33d5d7c1e192f0b7c06397bb88",
"sha256:978631ec51a6bbc0b7e58f23b68a8ce9e5f09721940933e9c217068388789fe5",
"sha256:9b4d4a5dbee05a2c390bf212e78b99434efec37b17a4bff42f50285c5c8c9647",
"sha256:ab0b005721cc0039e885ac3503825661bd9810b15d4f374e473f8c89b7d5460c",
"sha256:af653022a0c25ef2e3ffb2c673a50e5a0d02fecc41608f4954176f1933b12359",
"sha256:b0cc66c74c797e1db750aaa842ad5b8b78e14805a9b5d1348dc603612d3e3ff5",
"sha256:b424563394c369a804ecbee9b06dfb34997f19d00b3518e39f83a5642618397d",
"sha256:c138abae3a12a94c75c10499f1cbae81294a6f983b3af066390adee73f433028",
"sha256:c6cd67722619e4d55fdb42ead64ed8843d64638e9c07f4011163e46bc512cf01",
"sha256:c91fc8e8fd78af553f98bc7f2a1d8db977334e4eea302a4bfd75b9461c2d8904",
"sha256:cad399780053fb383dc067475135e41c9fe7d901a97dd5d9c5dfb5611afc0d7d",
"sha256:cb90f60e03d563ca2445099edf605c16ed1d5b15182d21831f58460c48bffb93",
"sha256:dad80b45c22e05b259e33ddd458e9e2ba099c86ccf4e88db7bbab4b747b18d06",
"sha256:dd3db61b8fe5be220eee484a17233287d0be6932d056cf5738225b9c05ef4fff",
"sha256:e28d62e59a4dbd1d22e747f57d4f00c459af22181f0b2f787ea83f5a876d7c76",
"sha256:e909df4053064a97f1e6565153ff8bb389af12c5c8d29c343308760890560aff",
"sha256:f3ffef566ac88f75967d7abd852ed5f182da252d23fac11b4766da3957766759",
"sha256:fc3c9babc1e1faefd62704bb46a69f359a9819eb0292e40df3fb6e3574715cd4",
"sha256:fe19d8bc5536a91a24a8133328880a41831b6c5df54599a8417b62fe015d3053"
],
"markers": "python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1'",
"version": "==44.0.3"
},
"ecdsa": {
"hashes": [
"sha256:30638e27cf77b7e15c4c4cc1973720149e1033827cfd00661ca5c8cc0cdb24c3",
"sha256:478cba7b62555866fcb3bb3fe985e06decbdb68ef55713c4e5ab98c57d508e61"
],
"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": {
"hashes": [
"sha256:87cc5225657524ec6361354be928adfd56bcf2a3dcc646c40f8f094c39c07db4",
"sha256:892b7361a719f4945237da8ccf754e9513db32f5628852785aea108dcd250093"
],
"version": "==2.3.0"
},
"mpremote": {
"hashes": [
"sha256:42691ff8f7ea4b5f2fc1b51de99609995d383671a4b4d4daad8cbd486d26aa23",
"sha256:d0dcd8ab364d87270e1766308882e536e541052efd64aadaac83bc7ebbea2815"
],
"index": "pypi",
"markers": "python_version >= '3.4'",
"version": "==1.25.0"
},
"pycparser": {
"hashes": [
"sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6",
"sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"
],
"markers": "python_version >= '3.8'",
"version": "==2.22"
},
"pyserial": {
"hashes": [
"sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb",
"sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0"
],
"index": "pypi",
"version": "==3.5"
},
"pyyaml": {
"hashes": [
"sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff",
"sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48",
"sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086",
"sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e",
"sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133",
"sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5",
"sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484",
"sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee",
"sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5",
"sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68",
"sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a",
"sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf",
"sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99",
"sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8",
"sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85",
"sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19",
"sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc",
"sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a",
"sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1",
"sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317",
"sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c",
"sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631",
"sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d",
"sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652",
"sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5",
"sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e",
"sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b",
"sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8",
"sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476",
"sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706",
"sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563",
"sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237",
"sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b",
"sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083",
"sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180",
"sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425",
"sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e",
"sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f",
"sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725",
"sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183",
"sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab",
"sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774",
"sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725",
"sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e",
"sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5",
"sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d",
"sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290",
"sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44",
"sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed",
"sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4",
"sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba",
"sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12",
"sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"
],
"markers": "python_version >= '3.8'",
"version": "==6.0.2"
},
"reedsolo": {
"hashes": [
"sha256:2b6a3e402a1ee3e1eea3f932f81e6c0b7bbc615588074dca1dbbcdeb055002bd",
"sha256:c1359f02742751afe0f1c0de9f0772cc113835aa2855d2db420ea24393c87732"
],
"version": "==1.7.0"
},
"six": {
"hashes": [
"sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274",
"sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'",
"version": "==1.17.0"
}
},
"develop": {}
}

View File

@@ -1,19 +1,8 @@
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')
wifi.ap(name, '')

View File

@@ -1,55 +1,23 @@
import asyncio
from settings import Settings
from web import web
from patterns import Patterns
import gc
import utime
import machine
import ntptime
import time
import wifi
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.set_brightness(int(settings["brightness"]))
patterns.set_delay(int(settings["delay"]))
w = web(settings, patterns)
print(settings)
# start the server in a bacakground task
print("Starting")
w = web(settings)
server = asyncio.create_task(w.start_server(host="0.0.0.0", port=80))
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
while True:
#print(time.localtime())
# gc.collect()
gc.collect()
for i in range(60):
wdt.feed()
wdt.feed()
await asyncio.sleep_ms(500)
# cleanup before ending the application
await server
asyncio.run(main())

291
src/patterns.py Normal file
View File

@@ -0,0 +1,291 @@
from machine import Pin
from neopixel import NeoPixel
import utime
import random
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
}
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()
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
self.pattern_step = 0
def set_delay(self, delay):
self.delay = delay
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
return True
return False
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
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:
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 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:
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)
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.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
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):
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
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))

View File

@@ -1,4 +1,7 @@
import json
import wifi
import ubinascii
import machine
class Settings(dict):
SETTINGS_FILE = "/settings.json"
@@ -8,13 +11,7 @@ class Settings(dict):
self.load() # Load settings from file during initialization
def set_defaults(self):
self["num_leds"] = 50
self["selected_pattern"] = "blink"
self["color1"] = "#000f00"
self["color2"] = "#0f0000"
self["delay"] = 100
self["brightness"] = 100
self["wifi"] = {"ssid": "", "password": ""}
self = {}
def save(self):
try:
@@ -34,20 +31,4 @@ class Settings(dict):
except Exception as e:
print(f"Error loading settings")
self.set_defaults()
# Example usage
def main():
settings = Settings()
print(f"Number of LEDs: {settings['num_leds']}")
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()
self.save()

View File

@@ -0,0 +1,143 @@
import { getWebSocket } from "./websocket.js";
export class LightComponent extends HTMLElement {
constructor() {
super();
// Create a shadow DOM for encapsulation
const shadow = this.attachShadow({ mode: "open" });
// Create the content for the component
const style = document.createElement("style");
style.textContent = `
:host {
display: block;
width: 100px;
height: 100px;
background-color: #4caf50;
color: white;
text-align: center;
line-height: 100px;
cursor: grab;
position: absolute;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
:host:active {
cursor: grabbing;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
}
.color-picker {
position: absolute;
top: 0;
right: 0;
width: 50px;
height: 50px;
cursor: pointer;
}
`;
// Create the main content (draggable area)
const content = document.createElement("div");
content.textContent = this.textContent || "Light Me Up!";
content.style.position = "absolute";
content.style.top = "0";
content.style.left = "0";
content.style.width = "100%";
content.style.height = "100%";
content.style.display = "flex";
content.style.justifyContent = "center";
content.style.alignItems = "center";
// Create the color picker
const colorPicker = document.createElement("input");
colorPicker.type = "color";
colorPicker.classList.add("color-picker");
colorPicker.value = "#4caf50"; // Default color
colorPicker.addEventListener("input", () => {
this.style.backgroundColor = colorPicker.value;
this.dispatchEvent(
new CustomEvent("color-change", {
detail: { lightId: this.lightId, color: colorPicker.value },
}),
);
});
// Append the style, content, and color picker to the shadow DOM
shadow.appendChild(style);
shadow.appendChild(content);
shadow.appendChild(colorPicker);
// Add event listeners for drag-and-drop
content.addEventListener("mousedown", this.handleMouseDown.bind(this));
document.addEventListener("mousemove", this.handleMouseMove.bind(this));
document.addEventListener("mouseup", this.handleMouseUp.bind(this));
}
// Track the initial mouse position and component position
handleMouseDown(event) {
event.preventDefault();
// Get the initial mouse position relative to the component
this.initialMouseX = event.clientX;
this.initialMouseY = event.clientY;
// Get the initial position of the component
const rect = this.getBoundingClientRect();
this.initialComponentX = rect.left;
this.initialComponentY = rect.top;
// Add a class to indicate dragging
this.classList.add("dragging");
}
// Update the component's position as the mouse moves
handleMouseMove(event) {
if (!this.classList.contains("dragging")) return;
// Calculate the new position of the component
const newX = this.initialComponentX + (event.clientX - this.initialMouseX);
const newY = this.initialComponentY + (event.clientY - this.initialMouseY);
// Update the component's position
this.style.left = `${newX}px`;
this.style.top = `${newY}px`;
}
// Stop dragging when the mouse is released
handleMouseUp() {
// Check if the component is being dragged
if (!this.classList.contains("dragging")) {
return; // Do nothing if not dragging
}
// Remove the dragging class
this.classList.remove("dragging");
// Get the current position of the component
const rect = this.getBoundingClientRect();
const newX = rect.left;
const newY = rect.top;
// Dispatch an event to notify the parent about the updated position
this.dispatchEvent(
new CustomEvent("position-change", {
detail: { lightId: this.lightId, x: newX, y: newY },
}),
);
}
// Add a property to hold the lightId
set lightId(id) {
this._lightId = id;
}
get lightId() {
return this._lightId;
}
}
// Define the custom element
customElements.define("light-component", LightComponent);

View File

@@ -0,0 +1,90 @@
// light-components.js
import { LightComponent } from "./light-component.js";
import { getWebSocket } from "./websocket.js";
// Map to store backend IDs and their corresponding components
const componentMap = new Map();
// Function to create and configure a light component
function createLightComponent(data, key, appContainer) {
const lightComponent = document.createElement("light-component");
lightComponent.style.left = `${data.x}px`; // Set the x position
lightComponent.style.top = `${data.y}px`; // Set the y position
lightComponent.style.backgroundColor = data.settings?.color || "#4caf50"; // Set the background color
lightComponent.textContent = data.name || "Light Me Up!"; // Set the text content
// Set the lightId property
lightComponent.lightId = key; // Use the backend ID as the lightId
// Store the component in the map
componentMap.set(key, lightComponent);
// Append the light component to the container
appContainer.appendChild(lightComponent);
// Handle position change
lightComponent.addEventListener("position-change", (event) => {
const { lightId, x, y } = event.detail;
updatePositionOnServer(lightId, x, y);
});
// Handle color change
lightComponent.addEventListener("color-change", (event) => {
const { lightId, color } = event.detail;
sendColorToServer(lightId, color);
});
// Example: Add a click event listener to the light-component
lightComponent.addEventListener("click", () => {
console.log(`Light component clicked! ID: ${lightComponent.lightId}`);
});
}
// Function to create light components from the fetched data
export function createLightComponents(appContainer, lightData) {
for (const key in lightData) {
if (lightData.hasOwnProperty(key)) {
const light = lightData[key];
createLightComponent(light, key, appContainer); // Pass the backend ID
}
}
}
// Function to send the updated position to the server via a PATCH request
async function updatePositionOnServer(componentId, x, y) {
try {
const response = await fetch(`/light/${componentId}`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ x, y }),
});
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
console.log(
`Updated position for component ${componentId}: x=${x}, y=${y}`,
);
} catch (error) {
console.error("Error updating position on server:", error);
}
}
// Function to send the selected color to the server via WebSocket
function sendColorToServer(componentId, color) {
const websocket = getWebSocket();
const message = JSON.stringify({
componentId,
color,
});
if (websocket.readyState === WebSocket.OPEN) {
websocket.send(message);
console.log("Sent color to server:", message);
} else {
console.warn("WebSocket is not open. Unable to send color.");
}
}

View File

@@ -1,75 +0,0 @@
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);
}
}

View File

@@ -1,156 +1,32 @@
let delayTimeout;
let brightnessTimeout;
let colorTimeout;
let color2Timeout;
let socket;
// main.js
import { createLightComponents } from "./light-components.js";
import { getWebSocket } from "./websocket.js";
const host = window.location.host;
// Wait for the DOM to be fully loaded
document.addEventListener("DOMContentLoaded", async () => {
// Select the container where the light-components will be added
const appContainer = document.getElementById("app");
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);
// Fetch the JSON data from the /light endpoint
try {
const response = await fetch("/light");
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
}
const lightData = await response.json();
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);
}
}
// Create and configure light components
createLightComponents(appContainer, lightData);
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);
}
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);
}
async function updatePattern(pattern) {
await post("/pattern", { pattern }); // Send as JSON
//socket.send(JSON.stringify({"selected_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);
}
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);
}
async function updateNumLeds(event) {
event.preventDefault();
const numLeds = document.getElementById('num_leds').value;
await post('/num_leds', { num_leds: numLeds }); // Send as JSON
}
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;
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");
}
}
function createPatternButtons(patterns) {
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);
// Initialize WebSocket connection
const websocket = getWebSocket();
websocket.addEventListener("open", () => {
console.log("WebSocket connection established.");
});
}
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.querySelectorAll(".pattern_button").forEach(button => {
console.log(button.value);
button.addEventListener('click', async event => {
event.preventDefault();
await updatePattern(button.value);
});
});
socket = new WebSocket(`ws://${host}/settings`)
websocket.addEventListener("message", (event) => {
console.log("Message from server:", event.data);
});
} catch (error) {
console.error("Error fetching light data:", error);
}
});
// 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';
}
function selectControls() {
const settingsMenu = document.getElementById('settings_menu');
controls = document.getElementById('controls');
settingsMenu.style.display = 'none';
controls.style.display = 'block';
}

20
src/static/styles.css Normal file
View File

@@ -0,0 +1,20 @@
/* Default styles for the light component */
light-component {
display: block;
width: 100px;
height: 100px;
background-color: #4caf50;
color: white;
text-align: center;
line-height: 100px;
cursor: grab;
position: absolute;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
/* Styles when the component is being dragged */
light-component:active {
cursor: grabbing;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
}

26
src/static/websocket.js Normal file
View File

@@ -0,0 +1,26 @@
// websocket.js
let websocket = null;
export function getWebSocket() {
if (!websocket) {
// Replace 'ws://your-server-url' with your WebSocket server URL
websocket = new WebSocket(`ws://${window.location.host}/ws`);
// Handle WebSocket connection open
websocket.onopen = () => {
console.log("WebSocket connection established");
};
// Handle WebSocket connection close
websocket.onclose = () => {
console.log("WebSocket connection closed");
};
// Handle WebSocket errors
websocket.onerror = (error) => {
console.error("WebSocket error:", error);
};
}
return websocket;
}

View File

@@ -1,71 +1,16 @@
{% args settings, patterns %}
<!DOCTYPE html>
<!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 %}
<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>
<!-- 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>
<!-- 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>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Light Component</title>
<!-- Link to the external CSS file -->
<link rel="stylesheet" href="static/styles.css" />
</head>
<body>
<!-- The light-component will be added dynamically by main.js -->
<div id="app"></div>
<!-- Import the JavaScript files -->
<script type="module" src="static/main.js"></script>
</body>
</html>

View File

@@ -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>
"""

View File

@@ -1,22 +1,113 @@
from microdot import Microdot, send_file, Response
from microdot.utemplate import Template
from microdot.websocket import with_websocket
import json
import wifi
def web(settings):
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_handler(request):
return Template('/index.html').render(settings=settings)
@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.route("/ws")
@with_websocket
async def ws(request, ws):
# Register the client's WebSocket connection
print("WebSocket connection established")
while True:
data = await ws.receive()
if data:
try:
# Parse the JSON message from the client
message = json.loads(data)
light = message.get("light")
if message["light"] in settings.get("lights"):
settings["lights"][light].update(message["settings"])
if message["save"]:
settings.save()
except json.JSONDecodeError:
print("Invalid JSON received")
else:
break
print("WebSocket connection closed")
@app.get("/light")
async def get_lights(request):
return json.dumps(settings)
@app.get("/light/<light>")
async def get_light(request, light):
light_data = settings.get(light, None)
if light_data:
return json.dumps(light_data), 200
else:
return json.dumps({"error": "Light not found"}), 404
@app.post("/light/<light>")
async def add_light(request, light):
try:
# Parse the JSON request body
data = request.json
# Check if the light already exists
if light in settings:
return json.dumps({"error": "Light already exists"}), 409
# Add the new light to the settings
settings[light] = data
print(settings)
settings.save()
return json.dumps(
{"message": "Light added successfully",
"light": light}
), 200
except Exception as e:
print(f"Exception: {e}")
return json.dumps({"error": "Invalid JSON request"}), 400
@app.patch("/light/<light>")
async def update_light(request, light):
try:
# Parse the JSON request body
data = request.json
print(light, data)
# Check if the light exists
if light not in settings:
return json.dumps({"error": "Light not found"}), 404
# Update the existing light with the provided data
settings[light].update(data)
settings.save() # Uncomment if using persistent storage
return json.dumps(
{"message": "Light updated successfully", "light": settings[light]}
), 200
except json.JSONDecodeError:
return json.dumps({"error": "Invalid JSON request"}), 400
@app.delete("/light/<light>")
async def del_light(request, light):
if light in settings:
# Remove the light from the settings
del settings[light]
settings.save()
return json.dumps({"message": "Light deleted successfully"})
else:
return json.dumps({"error": "Light not found"}), 404
return app
# Example usage
if __name__ == "__main__":
app = web(settings)
app.run()

View File

@@ -1,8 +1,5 @@
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:
@@ -26,21 +23,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')