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 wifi
import time
from settings import Settings from settings import Settings
print(wifi.ap('qwerty')) s = Settings()
name = s.get('name', 'led')
settings = Settings() wifi.ap(name, '')
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)

View File

@@ -1,55 +1,23 @@
import asyncio import asyncio
from settings import Settings from settings import Settings
from web import web from web import web
from patterns import Patterns
import gc import gc
import utime
import machine import machine
import ntptime
import time
import wifi
async def main(): async def main():
settings = Settings() 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") print("Starting")
w = web(settings)
server = asyncio.create_task(w.start_server(host="0.0.0.0", port=80)) server = asyncio.create_task(w.start_server(host="0.0.0.0", port=80))
wdt = machine.WDT(timeout=10000) wdt = machine.WDT(timeout=10000)
wdt.feed() wdt.feed()
async def tick():
while True:
patterns.tick()
await asyncio.sleep_ms(1)
asyncio.create_task(tick())
first = True
while True: while True:
#print(time.localtime()) gc.collect()
# gc.collect()
for i in range(60): for i in range(60):
wdt.feed() wdt.feed()
await asyncio.sleep_ms(500) await asyncio.sleep_ms(500)
# cleanup before ending the application # cleanup before ending the application
await server await server
asyncio.run(main()) 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 json
import wifi
import ubinascii
import machine
class Settings(dict): class Settings(dict):
SETTINGS_FILE = "/settings.json" SETTINGS_FILE = "/settings.json"
@@ -8,13 +11,7 @@ class Settings(dict):
self.load() # Load settings from file during initialization self.load() # Load settings from file during initialization
def set_defaults(self): def set_defaults(self):
self["num_leds"] = 50 self = {}
self["selected_pattern"] = "blink"
self["color1"] = "#000f00"
self["color2"] = "#0f0000"
self["delay"] = 100
self["brightness"] = 100
self["wifi"] = {"ssid": "", "password": ""}
def save(self): def save(self):
try: try:
@@ -34,20 +31,4 @@ class Settings(dict):
except Exception as e: except Exception as e:
print(f"Error loading settings") print(f"Error loading settings")
self.set_defaults() self.set_defaults()
self.save()
# 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()

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; // main.js
let brightnessTimeout; import { createLightComponents } from "./light-components.js";
let colorTimeout; import { getWebSocket } from "./websocket.js";
let color2Timeout;
let socket;
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) { // Fetch the JSON data from the /light endpoint
console.log(`POST to ${path}`, data); try {
try { const response = await fetch("/light");
const response = await fetch(path, { if (!response.ok) {
method: "POST", throw new Error(`HTTP error! Status: ${response.status}`);
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);
} }
} const lightData = await response.json();
async function get(path) { // Create and configure light components
try { createLightComponents(appContainer, lightData);
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);
}
}
async function updateColor(event) { // Initialize WebSocket connection
event.preventDefault(); const websocket = getWebSocket();
clearTimeout(colorTimeout); websocket.addEventListener("open", () => {
colorTimeout = setTimeout(async function() { console.log("WebSocket connection established.");
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);
}); });
} websocket.addEventListener("message", (event) => {
console.log("Message from server:", event.data);
document.addEventListener('DOMContentLoaded', async function() { });
document.getElementById('color').addEventListener('input', updateColor); } catch (error) {
document.getElementById('color2').addEventListener('input', updateColor2); console.error("Error fetching light data:", error);
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`)
}); });
// 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"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>LED Control</title> <title>Light Component</title>
<script src="static/main.js"></script> <!-- Link to the external CSS file -->
<link rel="stylesheet" href="static/main.css"> <link rel="stylesheet" href="static/styles.css" />
</head> </head>
<body> <body>
<h1>Control LEDs</h1> <!-- The light-component will be added dynamically by main.js -->
<button onclick="selectControls()">Controls</button> <div id="app"></div>
<button onclick="selectSettings()">Settings</button> <!-- Import the JavaScript files -->
<script type="module" src="static/main.js"></script>
<!-- Main LED Controls --> </body>
<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>
</html> </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 import Microdot, send_file, Response
from microdot.utemplate import Template from microdot.utemplate import Template
from microdot.websocket import with_websocket from microdot.websocket import with_websocket
import json import json
import wifi
def web(settings): def web(settings):
app = Microdot() app = Microdot()
Response.default_content_type = 'text/html' Response.default_content_type = 'text/html'
@app.route('/') @app.route('/')
async def index(request): async def index_handler(request):
return Template('/index.html').render(settings=settings, patterns=patterns.patterns.keys()) return Template('/index.html').render(settings=settings)
@app.route("/static/<path:path>") @app.route("/static/<path:path>")
def static(request, path): def static_handler(request, path):
if '..' in path: if '..' in path:
# Directory traversal is not allowed # Directory traversal is not allowed
return 'Not found', 404 return 'Not found', 404
return send_file('static/' + path) 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 return app
# Example usage
if __name__ == "__main__":
app = web(settings)
app.run()

View File

@@ -1,8 +1,5 @@
import network import network
from machine import Pin
from time import sleep from time import sleep
import ubinascii
from settings import Settings
def connect(ssid, password, ip, gateway): def connect(ssid, password, ip, gateway):
if ssid is None or password is None: if ssid is None or password is None:
@@ -26,21 +23,16 @@ def connect(ssid, password, ip, gateway):
return None return None
def ap(password): def ap(ssid, password):
ap_if = network.WLAN(network.AP_IF) ap_if = network.WLAN(network.AP_IF)
ap_mac = ap_if.config('mac') ap_mac = ap_if.config('mac')
ssid = f"led-{ubinascii.hexlify(ap_mac).decode()}"
print(ssid) print(ssid)
ap_if.active(True) 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(False)
ap_if.active(True) ap_if.active(True)
print(ap_if.ifconfig()) print(ap_if.ifconfig())
def get_mac():
ap_if = network.WLAN(network.AP_IF)
return ap_if.config('mac')