diff --git a/Pipfile b/Pipfile index c05731c..0ba5375 100644 --- a/Pipfile +++ b/Pipfile @@ -11,6 +11,7 @@ watchfiles = "*" fastapi = "*" uvicorn = "*" flask = "*" +serial = "*" [dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock index 8e9fe15..25c82e9 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "921fc0268aaeb27ac977902942dd25f0f84ea35bcbbee0412a4d7c801652eb67" + "sha256": "5d970f8c0ea9e8ffa98cf0ea5f791161589a97d953d2629da026d01fa7a8bce7" }, "pipfile-spec": 6, "requires": { @@ -151,11 +151,11 @@ }, "bitstring": { "hashes": [ - "sha256:69d1587f0ac18dc7d93fc7e80d5f447161a33e57027e726dc18a0a8bacf1711a", - "sha256:a08bc09d3857216d4c0f412a1611056f1cc2b64fd254fb1e8a0afba7cfa1a95a" + "sha256:e682ac522bb63e041d16cbc9d0ca86a4f00194db16d0847c7efe066f836b2e37", + "sha256:feac49524fcf3ef27e6081e86f02b10d2adf6c3773bf22fbe0e7eea9534bc737" ], "markers": "python_version >= '3.8'", - "version": "==4.3.1" + "version": "==4.4.0" }, "blinker": { "hashes": [ @@ -265,84 +265,89 @@ }, "cryptography": { "hashes": [ - "sha256:01df4f50f314fbe7009f54046e908d1754f19d0c6d3070df1e6268c5a4af09fa", - "sha256:0563655cb3c6d05fb2afe693340bc050c30f9f34e15763361cf08e94749401fc", - "sha256:078e5f06bd2fa5aea5a324f2a09f914b1484f1d0c2a4d6a8a28c74e72f65f2da", - "sha256:0a9ad24359fee86f131836a9ac3bffc9329e956624a2d379b613f8f8abaf5255", - "sha256:2067461c80271f422ee7bdbe79b9b4be54a5162e90345f86a23445a0cf3fd8a2", - "sha256:281526e865ed4166009e235afadf3a4c4cba6056f99336a99efba65336fd5485", - "sha256:2d08bc22efd73e8854b0b7caff402d735b354862f1145d7be3b9c0f740fef6a0", - "sha256:3c268a3490df22270955966ba236d6bc4a8f9b6e4ffddb78aac535f1a5ea471d", - "sha256:3d425eacbc9aceafd2cb429e42f4e5d5633c6f873f5e567077043ef1b9bbf616", - "sha256:44cc0675b27cadb71bdbb96099cca1fa051cd11d2ade09e5cd3a2edb929ed947", - "sha256:47bcd19517e6389132f76e2d5303ded6cf3f78903da2158a671be8de024f4cd0", - "sha256:485e2b65d25ec0d901bca7bcae0f53b00133bf3173916d8e421f6fddde103908", - "sha256:5aa3e463596b0087b3da0dbe2b2487e9fc261d25da85754e30e3b40637d61f81", - "sha256:5f14fba5bf6f4390d7ff8f086c566454bff0411f6d8aa7af79c88b6f9267aecc", - "sha256:62217ba44bf81b30abaeda1488686a04a702a261e26f87db51ff61d9d3510abd", - "sha256:6225d3ebe26a55dbc8ead5ad1265c0403552a63336499564675b29eb3184c09b", - "sha256:6bb5157bf6a350e5b28aee23beb2d84ae6f5be390b2f8ee7ea179cda077e1019", - "sha256:728fedc529efc1439eb6107b677f7f7558adab4553ef8669f0d02d42d7b959a7", - "sha256:766330cce7416c92b5e90c3bb71b1b79521760cdcfc3a6a1a182d4c9fab23d2b", - "sha256:812815182f6a0c1d49a37893a303b44eaac827d7f0d582cecfc81b6427f22973", - "sha256:829c2b12bbc5428ab02d6b7f7e9bbfd53e33efd6672d21341f2177470171ad8b", - "sha256:82a62483daf20b8134f6e92898da70d04d0ef9a75829d732ea1018678185f4f5", - "sha256:8a15fb869670efa8f83cbffbc8753c1abf236883225aed74cd179b720ac9ec80", - "sha256:8bf75b0259e87fa70bddc0b8b4078b76e7fd512fd9afae6c1193bcf440a4dbef", - "sha256:91627ebf691d1ea3976a031b61fb7bac1ccd745afa03602275dda443e11c8de0", - "sha256:93d8291da8d71024379ab2cb0b5c57915300155ad42e07f76bea6ad838d7e59b", - "sha256:9b34d8ba84454641a6bf4d6762d15847ecbd85c1316c0a7984e6e4e9f748ec2e", - "sha256:9b4d17bc7bd7cdd98e3af40b441feaea4c68225e2eb2341026c84511ad246c0c", - "sha256:9c2da296c8d3415b93e6053f5a728649a87a48ce084a9aaf51d6e46c87c7f2d2", - "sha256:a05177ff6296644ef2876fce50518dffb5bcdf903c85250974fc8bc85d54c0af", - "sha256:a90e43e3ef65e6dcf969dfe3bb40cbf5aef0d523dff95bfa24256be172a845f4", - "sha256:a9556ba711f7c23f77b151d5798f3ac44a13455cc68db7697a1096e6d0563cab", - "sha256:b1de0ebf7587f28f9190b9cb526e901bf448c9e6a99655d2b07fff60e8212a82", - "sha256:be8c01a7d5a55f9a47d1888162b76c8f49d62b234d88f0ff91a9fbebe32ffbc3", - "sha256:bfd019f60f8abc2ed1b9be4ddc21cfef059c841d86d710bb69909a688cbb8f59", - "sha256:c236a44acfb610e70f6b3e1c3ca20ff24459659231ef2f8c48e879e2d32b73da", - "sha256:c411f16275b0dea722d76544a61d6421e2cc829ad76eec79280dbdc9ddf50061", - "sha256:c92010b58a51196a5f41c3795190203ac52edfd5dc3ff99149b4659eba9d2085", - "sha256:d5a45ddc256f492ce42a4e35879c5e5528c09cd9ad12420828c972951d8e016b", - "sha256:daa392191f626d50f1b136c9b4cf08af69ca8279d110ea24f5c2700054d2e263", - "sha256:dc1272e25ef673efe72f2096e92ae39dea1a1a450dd44918b15351f72c5a168e", - "sha256:dce1e4f068f03008da7fa51cc7abc6ddc5e5de3e3d1550334eaf8393982a5829", - "sha256:dd5aba870a2c40f87a3af043e0dee7d9eb02d4aff88a797b48f2b43eff8c3ab4", - "sha256:de0f5f4ec8711ebc555f54735d4c673fc34b65c44283895f1a08c2b49d2fd99c", - "sha256:df4a817fa7138dd0c96c8c8c20f04b8aaa1fac3bbf610913dcad8ea82e1bfd3f", - "sha256:e07ea39c5b048e085f15923511d8121e4a9dc45cee4e3b970ca4f0d338f23095", - "sha256:eeeb2e33d8dbcccc34d64651f00a98cb41b2dc69cef866771a5717e6734dfa32", - "sha256:fa0900b9ef9c49728887d1576fd8d9e7e3ea872fa9b25ef9b64888adc434e976", - "sha256:fdc3daab53b212472f1524d070735b2f0c214239df131903bae1d598016fa822" + "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72", + "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235", + "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9", + "sha256:2ae6971afd6246710480e3f15824ed3029a60fc16991db250034efd0b9fb4356", + "sha256:2b7a67c9cd56372f3249b39699f2ad479f6991e62ea15800973b956f4b73e257", + "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad", + "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4", + "sha256:3b4995dc971c9fb83c25aa44cf45f02ba86f71ee600d81091c2f0cbae116b06c", + "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614", + "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed", + "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31", + "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229", + "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0", + "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731", + "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b", + "sha256:4d8ae8659ab18c65ced284993c2265910f6c9e650189d4e3f68445ef82a810e4", + "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4", + "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263", + "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595", + "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1", + "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678", + "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48", + "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76", + "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0", + "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18", + "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d", + "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d", + "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1", + "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981", + "sha256:8456928655f856c6e1533ff59d5be76578a7157224dbd9ce6872f25055ab9ab7", + "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82", + "sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2", + "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4", + "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663", + "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c", + "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d", + "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a", + "sha256:bc84e875994c3b445871ea7181d424588171efec3e185dced958dad9e001950a", + "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d", + "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b", + "sha256:c3bcce8521d785d510b2aad26ae2c966092b7daa8f45dd8f44734a104dc0bc1a", + "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826", + "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee", + "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9", + "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648", + "sha256:d861ee9e76ace6cf36a6a89b959ec08e7bc2493ee39d07ffe5acb23ef46d27da", + "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2", + "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2", + "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87" ], "markers": "python_version >= '3.8' and python_full_version not in '3.9.0, 3.9.1'", - "version": "==46.0.4" + "version": "==46.0.5" }, "esptool": { "hashes": [ - "sha256:2ea9bcd7eb263d380a4fe0170856a10e4c65e3f38c757ebdc73584c8dd8322da" + "sha256:9c355b7d6331cc92979cc710ae5c41f59830d1ea29ec24c467c6005a092c06d6" ], "index": "pypi", - "markers": "python_version >= '3.10'", - "version": "==5.1.0" + "version": "==5.2.0" }, "fastapi": { "hashes": [ - "sha256:c8cdf7c2182c9a06bf9cfa3329819913c189dc86389b90d5709892053582db29", - "sha256:ed99383fd96063447597d5aa2a9ec3973be198e3b4fc10c55f15c62efdb21c60" + "sha256:46e2fc5745924b7c840f71ddd277382af29ce1cdb7d5eab5bf697e3fb9999c9e", + "sha256:d04115b508d936d254cea545b7312ecaa58a7b3a0f84952535b4c9afae7668cd" ], "index": "pypi", - "markers": "python_version >= '3.9'", - "version": "==0.128.3" + "version": "==0.135.1" }, "flask": { "hashes": [ - "sha256:bf656c15c80190ed628ad08cdfd3aaa35beb087855e2f494910aa3774cc4fd87", - "sha256:ca1d8112ec8a6158cc29ea4858963350011b5c846a414cdb7a954aa9e967d03c" + "sha256:0ef0e52b8a9cd932855379197dd8f94047b359ca0a78695144304cb45f87c9eb", + "sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c" ], "index": "pypi", - "markers": "python_version >= '3.9'", - "version": "==3.1.2" + "version": "==3.1.3" + }, + "future": { + "hashes": [ + "sha256:929292d34f5872e70396626ef385ec22355a1fae8ad29e1a734c3e43f9fbc216", + "sha256:bd2968309307861edae1458a4f8a4f3598c03be43b97521076aebf5d94c07b05" + ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.0.0" }, "h11": { "hashes": [ @@ -367,6 +372,14 @@ ], "version": "==2.3.0" }, + "iso8601": { + "hashes": [ + "sha256:6b1d3829ee8921c4301998c909f7829fa9ed3cbdac0d3b16af2d743aed1ba8df", + "sha256:aac4145c4dcb66ad8b648a02830f5e2ff6c24af20f4f482689be402db2429242" + ], + "markers": "python_version >= '3.7' and python_version < '4.0'", + "version": "==2.1.0" + }, "itsdangerous": { "hashes": [ "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", @@ -500,16 +513,15 @@ "sha256:6bb75774648091dad6833af4f86c5bf6505f8d7aec211380f9e6996c01d23cb5" ], "index": "pypi", - "markers": "python_version >= '3.4'", "version": "==1.27.0" }, "platformdirs": { "hashes": [ - "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", - "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31" + "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934", + "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868" ], "markers": "python_version >= '3.10'", - "version": "==4.5.1" + "version": "==4.9.4" }, "pycparser": { "hashes": [ @@ -758,11 +770,11 @@ }, "rich": { "hashes": [ - "sha256:08e67c3e90884651da3239ea668222d19bea7b589149d8014a21c633420dbb69", - "sha256:e712f11c1a562a11843306f5ed999475f09ac31ffb64281f73ab29ffdda8b3b8" + "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", + "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b" ], "markers": "python_full_version >= '3.8.0'", - "version": "==14.3.2" + "version": "==14.3.3" }, "rich-click": { "hashes": [ @@ -772,6 +784,14 @@ "markers": "python_version >= '3.8'", "version": "==1.9.7" }, + "serial": { + "hashes": [ + "sha256:542150a127ddbf5ed2acc3a6ac4ce807cbcdae3b197acf785bbda6565c94f848", + "sha256:e887f06e07e190e39174b694eee6724e3c48bd361be1d97964caef5d5b61c73b" + ], + "index": "pypi", + "version": "==0.0.97" + }, "starlette": { "hashes": [ "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74", @@ -780,6 +800,43 @@ "markers": "python_version >= '3.10'", "version": "==0.52.1" }, + "tibs": { + "hashes": [ + "sha256:01ea5258bdf942d21560dc07d532082cd04f07cfef65fedd58ae84f7d0d2562a", + "sha256:0a7ce857ef05c59dc61abadc31c4b9b1e3c62f9e5fb29217988c308936aea71e", + "sha256:130bc68ff500fc8185677df7a97350b5d5339e6ba7e325bc3031337f6424ede7", + "sha256:173dfbecb2309edd9771f453580c88cf251e775613461566b23dbd756b3d54cb", + "sha256:1906729038b85c3b4c040aa28a456d85bc976d0c5007177350eb73374ffa0fd0", + "sha256:1b56583db148e5094d781c3d746815dbcbb6378c6f813c8ce291efd4ab21da8b", + "sha256:1d5521cc6768bfa6282a0c591ba06b079ab91b5c7d5696925ad2abac59779a54", + "sha256:1f95d5db62960205a1e9eba73ce67dc14e7366ae080cd4e5b6f005ebd90faf02", + "sha256:29480bf03e3372a5f9cc59ea0541f76f8efd696d4f0d214715e94247c342a037", + "sha256:2a618de62004d9217d2d2ab0f7f9bbdd098c12642dc01f07b3fb00f0b5f3131a", + "sha256:42725200f1b02687ed6e6a1c01e0ec150dc829d21d901ffc74cc0ac4d821f57f", + "sha256:477608f9b87e24a22ab6d50b81da04a5cb59bfa49598ff7ec5165035a18fb392", + "sha256:4b7510235379368b7523f624d46e0680f3706e3a3965877a6583cdcb598b8bac", + "sha256:501728d096e10d9a165aa526743d47418a6bbfd7b084fa47ecb22be7641d3edb", + "sha256:63255749f937c5e6fedcc7d54e7bd359aef711017e6855f373b0510a14ee2215", + "sha256:6a9feed5931b881809a950eca0e01e757113e2383a2af06a3e6982f110c869e2", + "sha256:76746f01b3db9dbd802f5e615f11f68df7a29ecef521b082dca53f3fa7d0084f", + "sha256:77103a9f1af72ac4cf5006828d0fb21578d19ce55fd990e9a1c8e46fd549561f", + "sha256:7d6592ed93c6748acd39df484c1ee24d40ee247c2a20ca38ba03363506fd24f3", + "sha256:847709c108800ad6a45efaf9a040628278956938a4897f7427a2587013dc3b98", + "sha256:859f05315ffb307d3474c505d694f3a547f00730a024c982f5f60316a5505b3c", + "sha256:a61d36155f8ab8642e1b6744e13822f72050fc7ec4f86ec6965295afa04949e2", + "sha256:a883ca13a922a66b2c1326a9c188123a574741a72510a4bf52fd6f97db191e44", + "sha256:ac0aa2aae38f7325c91c261ce1d18f769c4c7033c98d6ea3ea5534585cf16452", + "sha256:ace018a057459e3dccd06a4aae1c5c8cd57e352b263dcef534ae39bf3e03b5cf", + "sha256:ad61df93b50f875b277ab736c5d37b6bce56f9abce489a22f4e02d9daa2966e3", + "sha256:b9535dc7b7484904a58b51bd8e64da7efbf1d8466ff7e84ed1d78f4ddc561c99", + "sha256:d4f3ff613d486650816bc5516760c0382a2cc0ca8aeddd8914d011bc3b81d9a2", + "sha256:e13b9c7ff2604b0146772025e1ac6f85c8c625bf6ac73736ff671eaf357dda41", + "sha256:f5eea45851c960628a2bd29847765d55e19a687c5374456ad2c8cf6410eb1efa", + "sha256:f70bd250769381c73110d6f24feaf8b6fcd44f680b3cb28a20ea06db3d04fb6f" + ], + "markers": "python_version >= '3.8'", + "version": "==0.5.7" + }, "typing-extensions": { "hashes": [ "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", @@ -798,12 +855,11 @@ }, "uvicorn": { "hashes": [ - "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea", - "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee" + "sha256:96c30f5c7abe6f74ae8900a70e92b85ad6613b745d4879eb9b16ccad15645359", + "sha256:9b1f190ce15a2dd22e7758651d9b6d12df09a13d51ba5bf4fc33c383a48e1775" ], "index": "pypi", - "markers": "python_version >= '3.10'", - "version": "==0.40.0" + "version": "==0.42.0" }, "watchfiles": { "hashes": [ @@ -918,16 +974,15 @@ "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf" ], "index": "pypi", - "markers": "python_version >= '3.9'", "version": "==1.1.1" }, "werkzeug": { "hashes": [ - "sha256:5111e36e91086ece91f93268bb39b4a35c1e6f1feac762c9c822ded0a4e322dc", - "sha256:6a548b0e88955dd07ccb25539d7d0cc97417ee9e179677d22c7041c8f078ce67" + "sha256:210c6bede5a420a913956b4791a7f4d6843a43b6fcee4dfa08a65e93007d0d25", + "sha256:7ddf3357bb9564e407607f988f683d72038551200c704012bb9a4c523d42f131" ], "markers": "python_version >= '3.9'", - "version": "==3.1.5" + "version": "==3.1.6" } }, "develop": {} diff --git a/dev.py b/dev.py index 6ea8abc..f5f8ad9 100755 --- a/dev.py +++ b/dev.py @@ -3,11 +3,12 @@ import subprocess import serial import sys +from pathlib import Path print(sys.argv) # Extract port (first arg if it's not a command) -commands = ["src", "lib", "ls", "reset", "follow", "db"] +commands = ["src", "lib", "ls", "reset", "follow", "db", "test"] port = None if len(sys.argv) > 1 and sys.argv[1] not in commands: port = sys.argv[1] @@ -51,3 +52,27 @@ for cmd in sys.argv[1:]: subprocess.call(["mpremote", "connect", port, "fs", "cp", "-r", "db", ":" ]) else: print("Error: Port required for 'db' command") + case "test": + if port: + if "all" in sys.argv[1:]: + test_files = sorted( + str(path) + for path in Path("test").rglob("*.py") + if path.is_file() + ) + failed = [] + for test_file in test_files: + print(f"Running {test_file}") + code = subprocess.call( + ["mpremote", "connect", port, "run", test_file] + ) + if code != 0: + failed.append((test_file, code)) + if failed: + print("Some tests failed:") + for test_file, code in failed: + print(f" {test_file} (exit {code})") + else: + subprocess.call(["mpremote", "connect", port, "run", "test/all.py"]) + else: + print("Error: Port required for 'test' command") diff --git a/docs/API.md b/docs/API.md index 2914ca6..bfdb0f1 100644 --- a/docs/API.md +++ b/docs/API.md @@ -48,17 +48,17 @@ Presets define LED patterns with their configuration. Each preset has a name and - **`pattern`** (required): Pattern type. Options: - `"off"` - Turn off all LEDs - - `"on"` - Solid color + - `"on"` - Solid colour - `"blink"` - Blinking pattern - - `"rainbow"` - Rainbow color cycle + - `"rainbow"` - Rainbow colour cycle - `"pulse"` - Pulse/fade pattern - - `"transition"` - Color transition + - `"transition"` - Colour transition - `"chase"` - Chasing pattern - `"circle"` - Circle loading pattern -- **`colors`** (optional): Array of hex color strings (e.g., `"#FF0000"` for red). Default: `["#FFFFFF"]` - - Colors are automatically converted from hex to RGB and reordered based on device color order setting - - Supports multiple colors for patterns that use them +- **`colors`** (optional): Array of hex colour strings (e.g., `"#FF0000"` for red). Default: `["#FFFFFF"]` + - Colours are automatically converted from hex to RGB and reordered based on device colour order setting + - Supports multiple colours for patterns that use them - **`delay`** (optional): Delay in milliseconds between pattern updates. Default: `100` @@ -74,7 +74,7 @@ Presets define LED patterns with their configuration. Each preset has a name and ### Pattern-Specific Parameters #### Rainbow -- **`n1`**: Step increment (how many color wheel positions to advance per update). Default: `1` +- **`n1`**: Step increment (how many colour wheel positions to advance per update). Default: `1` #### Pulse - **`n1`**: Attack time in milliseconds (fade in) @@ -86,8 +86,8 @@ Presets define LED patterns with their configuration. Each preset has a name and - **`delay`**: Transition duration in milliseconds #### Chase -- **`n1`**: Number of LEDs with first color -- **`n2`**: Number of LEDs with second color +- **`n1`**: Number of LEDs with first colour +- **`n2`**: Number of LEDs with second colour - **`n3`**: Movement amount on even steps (can be negative) - **`n4`**: Movement amount on odd steps (can be negative) @@ -235,7 +235,7 @@ All devices will start at step 10 and advance together on subsequent beats. 1. **Version Check**: Messages with `v != "1"` are rejected 2. **Preset Processing**: Presets are created or updated (upsert behavior) -3. **Color Conversion**: Hex colors are converted to RGB tuples and reordered based on device color order +3. **Colour Conversion**: Hex colours are converted to RGB tuples and reordered based on device colour order 4. **Selection**: Devices select their assigned preset, optionally with step value ## Best Practices @@ -244,20 +244,20 @@ All devices will start at step 10 and advance together on subsequent beats. 2. **Use "off" for sync**: Select "off" pattern to synchronize devices before starting patterns 3. **Beats for manual mode**: Send select messages repeatedly with same preset name to advance manual patterns 4. **Step for precision**: Use step parameter when exact synchronization is required -5. **Color format**: Always use hex strings (`"#RRGGBB"`), conversion is automatic +5. **Colour format**: Always use hex strings (`"#RRGGBB"`), conversion is automatic ## Error Handling - Invalid version: Message is ignored - Missing preset: Selection fails, device keeps current preset - Invalid pattern: Selection fails, device keeps current preset -- Missing colors: Pattern uses default white color +- Missing colours: Pattern uses default white colour - Invalid step: Step value is used as-is (may cause unexpected behavior) ## Notes -- Colors are automatically converted from hex strings to RGB tuples -- Color order reordering happens automatically based on device settings +- Colours are automatically converted from hex strings to RGB tuples +- Colour order reordering happens automatically based on device settings - Step counter wraps around (0-255 for rainbow, unbounded for others) - Manual mode patterns stop after one step/cycle, waiting for next beat - Auto mode patterns run continuously until changed diff --git a/src/p2p.py b/src/p2p.py deleted file mode 100644 index a00f6a2..0000000 --- a/src/p2p.py +++ /dev/null @@ -1,16 +0,0 @@ -import asyncio -import aioespnow -import json - -async def p2p(settings, patterns): - e = aioespnow.AIOESPNow() # Returns AIOESPNow enhanced with async support - e.active(True) - async for mac, msg in e: - try: - data = json.loads(msg) - except: - print(f"Failed to load espnow data {msg}") - continue - - if "names" not in data or settings.get("name") in data.get("names", []): - await settings.set_settings(data.get("settings", {}), patterns, data.get("save", False)) \ No newline at end of file diff --git a/src/preset.py b/src/preset.py index d00babc..6c9f8ab 100644 --- a/src/preset.py +++ b/src/preset.py @@ -19,8 +19,45 @@ class Preset: def edit(self, data=None): if not data: return False + aliases = { + "pattern": "p", + "colors": "c", + "delay": "d", + "brightness": "b", + "auto": "a", + } + int_fields = {"d", "b", "n1", "n2", "n3", "n4", "n5", "n6"} + allowed_fields = {"p", "c", "d", "b", "a", "n1", "n2", "n3", "n4", "n5", "n6"} for key, value in data.items(): - setattr(self, key, value) + key = aliases.get(key, key) + if key not in allowed_fields: + continue + if key in int_fields: + try: + parsed = int(value) + if key == "b": + parsed = max(0, min(255, parsed)) + elif key in ("d", "n1", "n2", "n3", "n4", "n5", "n6"): + parsed = max(0, parsed) + setattr(self, key, parsed) + except (TypeError, ValueError): + continue + elif key == "a": + if isinstance(value, bool): + self.a = value + elif isinstance(value, int): + self.a = bool(value) + elif isinstance(value, str): + lowered = value.lower() + if lowered in ("true", "1", "yes", "on"): + self.a = True + elif lowered in ("false", "0", "no", "off"): + self.a = False + elif key == "c": + if isinstance(value, (list, tuple)): + self.c = value + else: + setattr(self, key, value) return True @property diff --git a/src/utils.py b/src/utils.py index a2a924e..8343b13 100644 --- a/src/utils.py +++ b/src/utils.py @@ -33,21 +33,26 @@ def convert_and_reorder_colors(colors, settings_or_color_order): converted_colors = [] for color in colors: - # Convert "#RRGGBB" to (R, G, B) - if isinstance(color, str) and color.startswith("#"): - r = int(color[1:3], 16) - g = int(color[3:5], 16) - b = int(color[5:7], 16) - rgb = (r, g, b) + try: + # Convert "#RRGGBB" to (R, G, B) + if isinstance(color, str) and color.startswith("#") and len(color) == 7: + r = int(color[1:3], 16) + g = int(color[3:5], 16) + b = int(color[5:7], 16) + rgb = (r, g, b) + elif isinstance(color, (list, tuple)) and len(color) == 3: + # Already a tuple/list, just coerce and clamp. + rgb = tuple(max(0, min(255, int(x))) for x in color) + else: + # Unknown format: ignore safely. + continue + # Reorder based on device color order reordered = (rgb[channel_order[0]], rgb[channel_order[1]], rgb[channel_order[2]]) converted_colors.append(reordered) - elif isinstance(color, (list, tuple)) and len(color) == 3: - # Already a tuple/list, just reorder (JSON may use string numbers) - rgb = tuple(int(x) for x in color) - reordered = (rgb[channel_order[0]], rgb[channel_order[1]], rgb[channel_order[2]]) - converted_colors.append(reordered) - else: - # Keep as-is if not recognized format - converted_colors.append(color) + except (TypeError, ValueError, IndexError): + # Skip malformed color entries to avoid crashing pattern loops. + continue + if not converted_colors: + converted_colors.append((255, 255, 255)) return converted_colors