3 Commits

Author SHA1 Message Date
af9b63565a Remove web interface 2025-10-15 18:48:51 +13:00
e1b844241d Remove web interface 2025-10-15 18:48:15 +13:00
14b87f40ef Remove web interface 2025-10-15 18:47:23 +13:00
67 changed files with 648 additions and 7054 deletions

View File

@@ -1,71 +0,0 @@
# 8-LED Bar System Setup
This system supports 8 LED bars working together, each with unique names "100" through "107".
## Quick Setup
### 1. Configure Each LED Bar
Each LED bar needs a unique name. Run the configuration script on each bar:
```bash
python configure_bar.py
```
Then enter the bar name (100, 101, 102, etc.) when prompted.
### 2. Update Bar Names (Optional)
To change the bar names, edit `/home/jimmy/projects/lighting-controller/src/bar_config.py`:
```python
LED_BAR_NAMES = [
"100", # Bar 1
"101", # Bar 2
"102", # Bar 3
"103", # Bar 4
"104", # Bar 5
"105", # Bar 6
"106", # Bar 7
"107", # Bar 8
]
```
### 3. Default Settings
All bars use the same default settings defined in `bar_config.py`:
```python
DEFAULT_BAR_SETTINGS = {
"pattern": "pulse",
"delay": 100,
"colors": [(0, 255, 0)], # Default green
"brightness": 100,
"num_leds": 200,
"n1": 10,
"n2": 10,
"n3": 1,
"n": 0,
}
```
## How It Works
1. **Lighting Controller** sends ESP-NOW messages to all bars simultaneously
2. **Each LED Bar** listens for messages addressed to its unique name
3. **All bars** receive the same pattern/color/brightness settings
4. **Synchronized effects** across all 8 bars
## Current Features
- ✅ All bars show the same pattern simultaneously
- ✅ Individual bar addressing (100-107)
- ✅ Optimized JSON payloads with defaults deduplication
- ✅ Easy configuration via `bar_config.py`
- ✅ MIDI control for all bars
- ✅ n3 step rate functionality
## Future Enhancements
- Sequential patterns (bar 1 → bar 2 → bar 3...)
- Wave effects across bars
- Individual bar control
- Master/slave synchronization
- Physical arrangement awareness

View File

@@ -8,7 +8,6 @@ mpremote = "*"
pyserial = "*"
esptool = "*"
watchfiles = "*"
uvicorn = "*"
[dev-packages]
@@ -16,4 +15,5 @@ uvicorn = "*"
python_version = "3.12"
[scripts]
dev = "./dev.py"
dev = 'watchfiles "./dev.py /dev/ttyACM0 src reset follow"'

546
Pipfile.lock generated
View File

@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "53809b70ded7a2b3e577a8a4263fbadbb722d1e8d92eb016e134b0776fd40f6b"
"sha256": "7b8033c15743e27f2589635c75bd0bb86ffc3a725b179d7db9ef200119aa9164"
},
"pipfile-spec": 6,
"requires": {
@@ -26,142 +26,142 @@
},
"bitarray": {
"hashes": [
"sha256:002b73bf4a9f7b3ecb02260bd4dd332a6ee4d7f74ee9779a1ef342a36244d0cf",
"sha256:01e3ba46c2dee6d47a4ab22561a01d8ee6772f681defc9fcb357097a055e48cf",
"sha256:03dc877ec286b7f2813185ea6bc5f1f5527fd859e61038d38768883b134e06b3",
"sha256:03eeab48f376c3cd988add2b75c20d2d084b6fcc9a164adb0dc390ef152255b4",
"sha256:05ee46a734b5110c5ac483815da4379f7622f4316362872ec7c0ed16db4b0148",
"sha256:0751596f60f33df66245b2dafa3f7fbe13cb7ac91dd14ead87d8c2eec57cb3ed",
"sha256:08c114cf02a63e13ce6d70bc5b9e7bdcfa8d5db17cece207cfa085c4bc4a7a0c",
"sha256:0ed4a87eda16e2f95d536152c5acccae07841fbdda3b9a752f3dbf43e39f4d6b",
"sha256:101230b8074919970433ef79866570989157ade3421246d4c3afb7a994fdc614",
"sha256:11fcfdf272549a3d876f10d8422bcd5f675750aa746ce04ff04937ec3bb2329e",
"sha256:160f449bb91686f8fc9984200e78b8d793b79e382decf7eb1dc9948d7c21b36f",
"sha256:16426a843b1bc9c552a7c97d6d7555e69730c2de1e2f560503d3fc0e7f6d8005",
"sha256:1f1575cc0f66aa70a0bb5cb57c8d9d1b7d541d920455169c6266919bf804dc20",
"sha256:1f7a8fc5085450635a539c47c9fce6d441b4a973686f88fc220aa20e3921fe55",
"sha256:1fb0a46ae4b8d244a3fb80c3055717baa3dec6be17938e6871042a8d5b4ce670",
"sha256:2965fd8ba31b04c42e4b696fad509dc5ab50663efca6eb06bb3b6d08587f3a09",
"sha256:2b524306104c1296f1e91d74ee4ccbeeea621f6a13e44addf0bb630a1839fd72",
"sha256:2db04b165a57499fbcfe0eaa2f7752f118552bbcfab2163a43fef8d95f4ae745",
"sha256:3092f6bbf4a75b1e6f14a5b1030e27c435f341afeb23987115e45a25cc68ba91",
"sha256:30a2fc37698820cbf9b51d5f801219ef4bed828a04f3307072b8f983dc422a0e",
"sha256:3110b98c5dfb31dc1cf82d8b0c32e3fa6d6d0b268ff9f2a1599165770c1af80f",
"sha256:33f604bffd06b170637f8a48ddcf42074ed1e1980366ac46058e065ce04bfe2a",
"sha256:340c524c7c934b61d1985d805bffe7609180fb5d16ece6ce89b51aa535b936f2",
"sha256:37a6a8382864a1defb5b370b66a635e04358c7334054457bbbb8645610cd95b2",
"sha256:3875578748b484638f6ea776f534e9088cfb15eee131aac051036cba40fd5d05",
"sha256:38b0261483c59bb39ae9300ad46bf0bbf431ab604266382d986a349c96171b36",
"sha256:3b9a2eb7d2e0e9c2f25256d2663c0a2a4798fe3110e3ddbbb1a7b71740b4de08",
"sha256:3bb3cf22c3c03ae698647e6766314149c9cf04aa2018d9f48d5efddc3ced2764",
"sha256:3db0648536f3e08afa7ceb928153c39913f98fd50a5c3adf92a4d0d4268f213e",
"sha256:3dc654da62b3a3027b7c922f7e9f4b27feaabd5d38b2a98ea98de5e8107c72f2",
"sha256:4079857566077f290d35e23ff0e8ba593069c139ae85b0d152b9fa476494f50a",
"sha256:44f468fb4857fff86c65bec5e2fb67067789e40dad69258e9bb78fc6a6df49e7",
"sha256:45660e2fabcdc1bab9699a468b312f47956300d41d6a2ea91c8f067572aaf38a",
"sha256:477b9456eb7d70f385dc8f097a1d66ee40771b62e47b3b3e33406dcfbc1c6a3b",
"sha256:481239cd0966f965c2b8fa78b88614be5f12a64e7773bb5feecc567d39bb2dd5",
"sha256:4a83d247420b147d4b3cba0335e484365e117dc1cfe5ab35acd6a0817ad9244f",
"sha256:53d2abeabb91a822e9d76420c9b44980edd2d6b21767c7bb9cb2b1b4cf091049",
"sha256:55c31bc3d2c9e48741c812ee5ce4607c6f33e33f339831c214d923ffc7777d21",
"sha256:567d6891cb1ddbfd0051fcff3cb1bb86efc82ec818d9c5f98c37d59c1d23cc96",
"sha256:57b9df5d38ab49c13eaa9e0152fdfa8501fc23987f6dcf421b73484bfe573918",
"sha256:59ddb8a9f47ec807009c69e582d0de1c86c005f9f614557f4cebc7b8ac9b7d28",
"sha256:61b9f3cf3a55322baed8f0532b73bce77d688a01446c179392c4056ab74eb551",
"sha256:639389b023315596e0293f85999645f47ec3dc28c892e51242dde6176c91486b",
"sha256:64d1143e90299ba8c967324840912a63a903494b1870a52f6675bda53dc332f7",
"sha256:6542e1cfe060badd160cd383ad93a84871595c14bb05fb8129f963248affd946",
"sha256:69687ef16d501c9217675af36fa3c68c009c03e184b07d22ba245e5c01d47e6b",
"sha256:6f7e1cdf0abb11718e655bb258920453b1e89c2315e9019f60f0775704b12a8c",
"sha256:7378055c9f456c5bb034ac313d9a9028fc6597619a0b16584099adba5a589fdb",
"sha256:78103afbd0a94ac4c1f0b4014545fd149b968d5ea423aaa3b1f6e2c3fc19423e",
"sha256:79038bf1a7b13d243e51f4b6909c6997c2ba2bffc45bcae264704308a2d17198",
"sha256:795b1760418ab750826420ae24f06f392c08e21dc234f0a369a69cc00444f8ec",
"sha256:7998dfb1e9e0255fb8553abb019c3e7f558925de4edc8604243775ff9dd3898d",
"sha256:7afc740ad45ee0e0cef055765faf64789c2c183eb4aa3ecb8cecdb4b607396b3",
"sha256:7b4a41dc183d7d16750634f65566205990f94144755a39f33da44c0350c3e1a8",
"sha256:7f825ebedcad87a2825ddb6cf62f6d7d5b7a56ddaf7c93eef4b974e7ddc16408",
"sha256:7f9f9bb2c5cc1f679605ebbeb72f46fc395d850b93fa7de7addd502a1dc66e99",
"sha256:7fdf059d4e3acec44f512ebe247718ae511fde632e2b06992022df8e637385a6",
"sha256:81e4648c09103bc18f488957c1e0863d2397bab6625c0e6771891f151ee0bd96",
"sha256:8489bff00a1f81ac0754355772e76775878c32a42f16f01d427c3645546761c4",
"sha256:851398428f5604c53371b72c5e0a28163274264ada4a08cd1eafe65fde1f68d0",
"sha256:87a29b8a4cc72af6118954592dcd4e49223420470ccc3f8091c255f6c7330bb1",
"sha256:8b8e07374d60040b24d1a158895d9758424db13be63d4b2fe1870e37f9dec009",
"sha256:8d4aa56782368269eb9402caf7378b2a5ada6f05eb9c7edc2362be258973fd7e",
"sha256:97c448a20aded59727261468873d9b11dfdcce5a6338a359135667d5e3f1d070",
"sha256:98373c273e01a5a7c17103ecb617de7c9980b7608351d58c72198e3525f0002e",
"sha256:98e4a17f55f3cbf6fe06cc79234269572f234467c8355b6758eb252073f78e6b",
"sha256:99124e39658b2f72d296819ec03418609dd4f1b275b00289c2f278a19da6f9c0",
"sha256:9ad0df7886cb9d6d2ff75e87d323108a0e32bdca5c9918071681864129ce8ea8",
"sha256:9bfdfe2e2af434d3f4e47250f693657334e34a7ec557cd703b129a814422b4b8",
"sha256:9faa4c6fcb19a31240ad389426699a99df481b6576f7286471e24efbf1b44dfc",
"sha256:a048e41e1cb0c1a37021269d02698e30d2a7cc9a0205dd3390e0807745b76dae",
"sha256:a05982bb49c73463cb0f0f4bed2d8da82631708a2c2d1926107ba99651b419ec",
"sha256:a23b5f13f9b292004e94b0b13fead4dae79c7512db04dc817ff2c2478298e04a",
"sha256:a393b0f881eff94440f72846a6f0f95b983594a0a50af81c41ed18107420d6a7",
"sha256:a3b6bd81c77d9925809b714980cd30b1831a86bd090316d37cab124d92af1daf",
"sha256:a43f4631ecb87bedc510568fef67db53f2a20c4a5953a9d1e07457e7b1d14911",
"sha256:a569c993942ac26c6c590639ed6712c6c9c3f0c8d287a067bf2a60eb615f3c6b",
"sha256:a5b89349f05431270d1ccc7321aaab91c42ff33f463868779e502438b7f0e668",
"sha256:ac39319e6322c2c093a660c02cea6bb3b1ae53d049b573d4781df8896e443e04",
"sha256:acc56700963f63307ac096689d4547e8061028a66bb78b90e42c5da2898898fb",
"sha256:b723f9d10f7d8259f010b87fa66e924bb4d67927d9dcff4526a755e9ee84fef4",
"sha256:b99a0347bc6131046c19e056a113daa34d7df99f1f45510161bc78bc8461a470",
"sha256:bc0880011b86f81c5353ce4abaeb2472d942ba2320985166a2a3dd4f783563a9",
"sha256:be2f40045432e8aa33d9fd5cb43c91b0c61d77d3d8810f88e84e2e46411c27a7",
"sha256:bebb17125373c499beea009cc5bced757bde52bcb3fa1d6335650e6c2d8111d7",
"sha256:befac6644c6f304a1b6a7948a04095682849c426cebcc44cb2459aa92d3e1735",
"sha256:c1f4880bcb6fb7a8e2ab89128032b3dcf59e1e877ff4493b11c8bf7c3a5b3df2",
"sha256:c3e014f7295b9327fa6f0b3e55a3fd485abac98be145b9597e0cdbb05c44ad07",
"sha256:c427dfcce13a8c814556dfe7c110b8ef61b8fab5fca0d856d4890856807321dc",
"sha256:c44cf0059633470c6bb415091def546adbeb5dcfa91cc3fcb1ac16593f14e52a",
"sha256:c4e04c12f507942f1ddf215cb3a08c244d24051cdd2ba571060166ce8a92be16",
"sha256:c65257899bb8faf6a111297b4ff0066324a6b901318582c0453a01422c3bcd5a",
"sha256:c6c48cf5a92244ef3df4161c8625ee1890bb3d931db9a9f3b699e61a037cd58a",
"sha256:c9bf2bf29854f165a47917b8782b6cf3a7d602971bf454806208d0cbb96f797a",
"sha256:ca4b6298c89b92d6b0a67dfc5f98d68ae92b08101d227263ef2033b9c9a03a72",
"sha256:cc76ad7453816318d794248fba4032967eaffd992d76e5d1af10ef9d46589770",
"sha256:cd7f6bfa2a36fb91b7dec9ddf905716f2ed0c3675d2b63c69b7530c9d211e715",
"sha256:d12c45da97b2f31d0233e15f8d68731cfa86264c9f04b2669b9fdf46aaf68e1f",
"sha256:d160173efdad8a57c22e422a034196df3d84753672c497aee2f94bd5b128f8dd",
"sha256:d2b1ed363a4ef5622dccbf7822f01b51195062c4f382b28c9bd125d046d0324c",
"sha256:d30e7daaf228e3d69cdd8b02c0dd4199cec034c4b93c80109f56f4675a6db957",
"sha256:d3f38373d9b2629dedc559e647010541cc4ec4ad9bea560e2eb1017e6a00d9ef",
"sha256:d7e274ac1975e55ebfb8166cce27e13dc99120c1d6ce9e490d7a716b9be9abb5",
"sha256:d877759842ff9eb16d9c2b8b497953a7d994d4b231c171515f0bf3a2ae185c0c",
"sha256:da3dfd2776226e15d3288a3a24c7975f9ee160ba198f2efa66bc28c5ba76d792",
"sha256:db0441e80773d747a1ed9edfb9f75e7acb68ce8627583bbb6f770b7ec49f0064",
"sha256:dbbaa147cf28b3e87738c624d390a3a9e2a5dfef4316f4c38b4ecaf3155a3eab",
"sha256:ddc646cec4899a137c134b13818469e4178a251d77f9f4b23229267e3da78cfb",
"sha256:df7cc9584614f495f474a5ded365cf72decbcee4efcdc888d2943f8a794c789e",
"sha256:dfde50ae55e075dcd5801e2c3ea0e749c849ed2cbbee991af0f97f1bdbadb2a6",
"sha256:e15e70a3cf5bb519e2448524d689c02ff6bcd4750587a517e2bffee06065bf27",
"sha256:e3572889fcb87e5ca94add412d8b365dbb7b59773a4362e52caa556e5fd98643",
"sha256:e39f5e85e1e3d7d84ac2217cd095b3678306c979e991532df47012880e02215d",
"sha256:e501bd27c795105aaba02b5212ecd1bb552ca2ee2ede53e5a8cb74deee0e2052",
"sha256:e62892645f6a214eefb58a42c3ed2501af2e40a797844e0e09ec1e400ce75f3d",
"sha256:e75eb1734046291c554d9addecca9a8785bdf5d53a64f525569f8549da863dde",
"sha256:e84cff8e8fe71903a6cf873fb3c8731df8bd7c1dac878e7a0fe19d8e2ef39aa9",
"sha256:ea60cf85b4e5a78b5a41eed3a65abc3839a50d915c6e0f6966cbcf81b85991bd",
"sha256:ec3fd30622180cbe2326d48c14a4ab7f98a504b104bdca7dda88b134adad6e31",
"sha256:eccc6829035c8b7b391a0aa124fade54932bb937dd1079f2740b9f1bde829226",
"sha256:eda67136343db96752e58ef36ac37116f36cba40961e79fd0e9bd858f5a09b38",
"sha256:ef5a99a8d1a5c47b4cf85925d1420fc4ee584c98be8efc548651447b3047242f",
"sha256:f0795e2be2aa8afd013635f30ffe599cc00f1bbaca2d1d19b6187b4d1c58fb44",
"sha256:f31d8c2168bf2a52e4539232392352832c2296e07e0e14b6e06a44da574099ba",
"sha256:f41a4b57cbc128a699e9d716a56c90c7fc76554e680fe2962f49cc4d8688b051",
"sha256:f583a1fb180a123c00064fab1a3bfb9d43e574b6474be1be3f6469e0331e3e2e",
"sha256:f7c531722e8c3901f6bb303db464cac98ab44ed422c0fd0c762baa4a8d49ffa1",
"sha256:f8ab90410b2ba5b8276657c66941bcaae556a38be8dd81630a7647e8735f0a20",
"sha256:fa05460dc4f57358680b977b4a254d331b24c8beb501319b998625fd6a22654b",
"sha256:fbe1ef622748d2edb3dd4fef933b934e90e479f9831dfe31bda3fdc16bf5287f",
"sha256:fdb7af369df317527d697c5bb37ab944bb9a17ea1a5e82e47d5c7c638f3ccdd6",
"sha256:fe1f1f4010244cb07f6a079854a12e1627e4fb9ea99d672f2ceccaf6653ca514",
"sha256:fe2493d3f49e314e573022ead4d8c845c9748979b7eb95e815429fe947c4bde2",
"sha256:ffd112646486a31ea5a45aa1eca0e2cd90b6a12f67e848e50349e324c24cc2e7"
"sha256:00628196dd3592972a5183194ab1475dadf9ef2a4cf3fd8c7c184a94934012e8",
"sha256:01d6dc548e7fe5c66913c2274f44855b0f8474935acff7811e84fe1f4024c94f",
"sha256:056fe779f01a867d572e071c0944ac2f3bf58d8bced326040f0bd060af33a209",
"sha256:080a7bf55c432abdae74f25dc3dbff407418346aeae1d43e31f65e8ef114f785",
"sha256:0956322bf4d5e2293e57600aa929c241edf1e209e94e12483bf58c5c691432db",
"sha256:0a6f9e897907757e9c2d722ae6c203d48a04826a14e1495e33935c8583c163a9",
"sha256:0ac446f557eb28e3f7c65372608810ff073840627e9037e22ed10bd081793a34",
"sha256:0b47843f2f288fa746dead4394591a3432a358aaad48240283fa230d6e74b0e7",
"sha256:11fc8bc65f964c7278deb1b7a69379dab3ecc90095f252deb17365637ebb274d",
"sha256:129165b68a3e0c2a633ed0d8557cf5ade24a0b37ca97d7805fa6fc5fb73c19d5",
"sha256:139963494fc3dd5caee5e38c0a03783ef50be118565e94b1dbb0210770f0b32d",
"sha256:157313a124287cbc8a11b55a75def0dd59e68badbc82c2dc2d204dc852742874",
"sha256:16d0edab54bb9d214319418f65bd15cfc4210ec41a16c3dd0b71e626c803212d",
"sha256:1971050b447023288a2b694a03b400bd5163829cd67b10f19e757fe87cd1161e",
"sha256:1c4e75bbf9ade3d2cdf1b607a8b353b17d9b3cf54e88b2a5a773f50ae6f1bfbc",
"sha256:1c9f36055a89b9517db66eb8e80137126bf629c767ceeade4d004e40bc8bcd99",
"sha256:2020102a40edd094c0aa80e09203af71c533c41f76ce3237c99fd194a473ea33",
"sha256:20febc849a1f858e6a57a7d47b323fe9e727c579ddd526d317ad8831748a66a8",
"sha256:220d4b8649ef54ac98e5e0e3dd92230247f67270d1524a8b31aa9859007affb0",
"sha256:22188943a29072b684cd7c99e0b2cfc0af317cea3366c583d820507e6d1f2ed4",
"sha256:222cb27ff05bc0aec72498d075dba1facec49a76a7da45740690cebbe3e81e43",
"sha256:243825f56b58bef28bfc602992a8c6d09bbc625628c195498d6020120d632a09",
"sha256:25060e7162e44242a449ed1a14a4e94b5aef340812754c443459f19c7954be91",
"sha256:26691454a6770628882b68fe74e9f84ca2a51512edd49cbb025b14df5a9dd85a",
"sha256:27d13c7b886afc5d2fc49d6e92f9c96b1f0a14dc7b5502520c29f3da7550d401",
"sha256:27eeee915258b105a21a4b0f8aebc5f77bb4dc4fb4063a09dd329fa1fdcbd234",
"sha256:29ed022189a7997de46cb9bd4e2e49d6163d4f8d78dea72ac5a0e0293b856810",
"sha256:2a324e3007afb5c667026f5235b35efe3c4a95f1b83cd93aa9fce67b42f08e7c",
"sha256:2c533c828d0007fac27cf45e5c1a711e5914dd469db5fe6be5f4e606bf2d7f63",
"sha256:30ba4fba3de1dca653de41c879349ec6ca521d85cff6a7ca5d2fdd8f76c93781",
"sha256:357e07c827bad01f98d0bd0dfdc722f483febeed39140fd75ffd016a451b60b9",
"sha256:3800f3c8c9780f281cf590543fd4b3278fea6988202273a260ecc58136895efb",
"sha256:3d6f3a94abd8b44b2bf346ca81ab2ff41ab9146c53905eedf5178b19d9fe53bf",
"sha256:3eb1390a8b062fe9125e5cc4c5eba990b5d383eec54f2b996e7ce73ac43150f9",
"sha256:407920e9318d94cc6c9611aaa5b5e5963a09f1cbfa17b16b66edea453b3754f4",
"sha256:42622c42c159ea4535bba7e1e3c97f1fec79505bc6873ae657dc0a8f861c60de",
"sha256:4695fcd37478988b1d0a16d5bc0df56dcb677fd5db37f1893d993fd3ebef914b",
"sha256:4798f6744fa2633666e17b4ea8ff70250781b52a25afdbf5ffb5e176c58848f1",
"sha256:4a5b0d277087a5bf261a607fc6ff4aaffcf80b300cd19b7a1e9754a4649f5fd4",
"sha256:4e297fd2e58afe17e33dd80c231c3a9d850279a2a8625aed1d39f9be9534809e",
"sha256:507e567aee4806576e20752f22533e8b7ec61e7e75062a7ce9222a0675aa0da6",
"sha256:50d702149747852923be60cae125285eca8d189d4c7d8832c0c958d4071a0f78",
"sha256:51947a00ae9924584fb14c0c1b1f4c1fd916d9abd6f47582f318ab9c9cb9f3d0",
"sha256:52328192d454ca2ddad09fbc088872b014c74b22ecdd5164717dc7e6442014fa",
"sha256:531e6dfec8058fcf5d69e863b61e6b28e3749b615a4dcc0ab8ad36307c4017fc",
"sha256:54bd71f14a5fa9bae73ef92f2e2be894dc36c7a6d1c4962e5969bd8a9aa39325",
"sha256:552a93be286ca485914777461b384761519db313e0a7f3012dca424c9610a4d5",
"sha256:583b46b3ba44121de5e87e95ae379932dc5fd2e37ebdf2c11a6d7975891425c1",
"sha256:5b58a672ec448fb36839a5fc7bf2b2f60df9a97b872d8bd6ca1a28da6126f5c7",
"sha256:5cfbdccddaa0ff07789e9e180db127906c676e479e05c04830cd458945de3511",
"sha256:5da4939e241301f5e1d18118695e8d2c300be90431b66bd43a00376acec45e1e",
"sha256:5dd9edcab8979a50c2c4dec6d5b66789fb6f630bb52ab90a4548111075a75e48",
"sha256:5e304f94c0353f6ae5711533b5793b3a45b17aa2c5b07e656649b0af4e0939b5",
"sha256:60408ec9c0bd76f1fa00d28034429a0316246d31069b982a86aec8d5c99e910a",
"sha256:62f71b268f14ee6cc3045b95441bfe0518cef1d0b2ffbc6f3e9758f786ff5a03",
"sha256:664d462a4c0783fd755fe3440f07b7e46d149859c96caacadf3f28890f19a8de",
"sha256:66d8b7a89fac6042f7df9ea97d97ed0f5e404281110a882e3babd909161f85b6",
"sha256:6755cfcfa7d8966e704d580c831e39818f85e7b2b7852ad22708973176f0009e",
"sha256:679856547f0b27b98811b73756bdf53769c23b045a6f95177cae634daabf1ddf",
"sha256:6841c08b51417f8ffe398b2828fc0593440c99525c868f640e0302476745320b",
"sha256:68f6e64d4867ee79e25c49d7f35b2b1f04a6d6f778176dcf5b759f3b17a02b2b",
"sha256:69d2d507c1174330c71c834b5d65e66181ad7b42b0d88b5b31804ee9b4f5dae7",
"sha256:6f0be27d06732e2833b672a8fcc32fa195bdb22161eb88f8890de15e30264a01",
"sha256:6f7d2dbe628f3db935622a5b80a5c4d95665cdefc4904372aa3c4d786289477f",
"sha256:72760411d60d8d76979a20ed3f15586d824db04668b581b86e61158c2b616db0",
"sha256:727f7a969416f02ef5c1256541e06f0836fb615022699fa8e2591e85296c5570",
"sha256:77d2368a06a86a18919c05a9b4b0ee9869f770e6a5f414b0fecc911870fe3974",
"sha256:79ab1c5f26f23e51d4a44c4397c8a3bf56c306c125dfab6b3eebdfa13d1dca6f",
"sha256:79db23eda81627132327ed292bd813a9af64399b98aaac3d42ad8deeed24cd5e",
"sha256:7c20d6e6cafce5027e7092beb2ac6eec0d71045d6318b34f36e1387a8c8859a3",
"sha256:7e0851a985a7b10f634188117c825ef99d63402555cca5bc32c7bfc5adaf0d6f",
"sha256:7e2e1ff784c2cdfd863bad31985851427f2d2796e445cec85080c7510cba4315",
"sha256:7f8b12424f8fdf29d1c0749c628bd1530cecfc77374935d096cccc0e4eada232",
"sha256:81e84054b22babcd6c5cc1eac0de2bfc1054ecdf742720cbfb36efbe89ec6c30",
"sha256:84bb57010a1ab76cf880424a2e0bce8dd26989849d2122ff073aa11bfc271c27",
"sha256:870ed23361e2918ab1ffc23fe0ab293abf3c372a68ee7387456d13da3e213008",
"sha256:8cf44b012e7493127ce7ca6e469138ac96b3295a117877d5469aabe7c8728d87",
"sha256:8d6c9bc14bacdfbfd51fed85f0576973eaaa7b30d81ef93264f8e22b86a9c9f7",
"sha256:8d759cecfa8aab4a1eb4e23b6420126b15c7743e85b33f389916bb98c4ecbb84",
"sha256:8ef3f0977c21190f949d5cfd71ded09de87d330c6d98bd5ecb5bb1135d666d0d",
"sha256:8f95daf0ce2b24815ddf62667229ba5dfc0cfee43eb43b2549766170d0f24ae9",
"sha256:911b4a16dce370657e5b8d8b6ba0fbb50dd5e2b24c4416f4b9e664503d3f0502",
"sha256:96117212229905da864794df9ea7bd54987c30a5dcbab3432edc3f344231adae",
"sha256:963cbcf296943f7017470d0b705e63e908f32b4f7dbe43f72c22f6fe1bd9ef66",
"sha256:975a118aa019d745f1398613b27fd8789f60a8cea057a00cdc1abedee123ffe6",
"sha256:9930853d451086c4c084d83a87294bdb0c5bc0fa4105a26c487ac09ea62e565b",
"sha256:99d16862e802e7c50c3b6cdd1bf041b6142335c9c2b426631f731257adfe5a15",
"sha256:9ed4a2852b3de7a64884afcc6936db771707943249a060aec8e551c16361d478",
"sha256:9f7796959c9c036a115d34696563f75d4a2912d3b97c15c15f2a36bdd5496ce9",
"sha256:a04b7a9017b8d0341ebbe77f61b74df1cf1b714f42b671a06f4912dc93d82597",
"sha256:a1b3c4ca3bec8e0ad9d32ce62444c5f3913588124a922629aa7d39357b2adf3f",
"sha256:a290a417608f50137bec731d1f22ff3efebac72845530807a8433b2db9358c95",
"sha256:a33f7c5acf44961f29018b13f0b5f5e1589ac0cfdf75a97c9774cf7ec84d09e0",
"sha256:a39be79a7c36e9a2e20376261c30deb3cdca86b50f7462ae9ff10a755c6720b9",
"sha256:a50a66fa34dd7f9dcdbc7602a1b7bf6f9ab030b4f43e892324193423d9ede180",
"sha256:a5ce1bdee102f7e60c075274df10b892d9ff5183ad6f5f515973eda8903dfe4c",
"sha256:a763dd33d6e27c9b4db3f8089a5fa39179a8a3cf48ce702b24a857d7c621333c",
"sha256:a773199dc42b5d02fcd46c8add552da2c4725ce2caa069527c7e27b5b6089e85",
"sha256:aa3c925502bd0b957a96a5619134bcdc0382ef73cffd40bad218ced3586bcf8d",
"sha256:aeb6db2f4ab54ac21a3851d05130a2aa78a6f6a5f14003f9ae3114fb8b210850",
"sha256:af670708e145b048ead87375b899229443f2d0b4af2d1450d7701c74cd932b03",
"sha256:afa24e5750c9b89ad5a7efef037efe49f4e339f20a94bf678c422c0c71e1207a",
"sha256:b02cc1cac9099c0ec72da09593e7fadb1b6cf88a101acc8153592c700d732d80",
"sha256:b37c9ea942395de029be270f0eca8c141eb14e8455941495cd3b6f95bbe465f4",
"sha256:b3b521e117ab991d6b3b830656f464b354a42dbea2ca16a0e7d93d573f7ab7ff",
"sha256:b5ad8261f47c2a72d0f676bc40f752db8cfdcab911e970753343836e41d5a9a7",
"sha256:b9616ea14917d06736339cf36bb9eaf4eb52110a74136b0dc5eff94e92417d22",
"sha256:b9a03767c937b621ee267507bc394df97fb2f8f61130f39f2033f3c6c191f124",
"sha256:b9ae0008cff25e154ef1e3975a1705d344e844ffdeb34c25b007fd48c876e95d",
"sha256:bdd6412c1f38da7565126b174f4e644f362e317ef0560fac1fb9d0c70900ff4d",
"sha256:bfc417e58f277e949ed662d9cd050ddbb00c0dea8a828abaccc93dc357b7a6d1",
"sha256:c15b9e37bbca59657e4dcc63ad068c821a4676def15f04742c406748a0a11b9c",
"sha256:c677849947d523a082be6e0b5c9137f443a54e951a1711ef003ec52910c41ece",
"sha256:c9d247fcc33c90f2758f4162693250341e3f38cd094f64390076ef33ad0887f9",
"sha256:ca643295bf5441dd38dadf7571ca4b63961820eedbffbe46ceba0893bf226203",
"sha256:ca87f639094c72268e17bc7f57c1225cc38f9e191a489a0134762e3fec402c1a",
"sha256:cc060bc17b9de27874997d612e37d52f72092f9b59d1e04284a90ed8113cadca",
"sha256:ccf4a73e07bfbd790443d6b3c1f1447ffda23cc9391e40c035d9b7d3514b57b8",
"sha256:cf36cadeb9c989f760a13058dbc455e5406ec3d2d247c705c8d4bc6dd1b0fcc6",
"sha256:d47e2bdeba4fb1986af2ba395ce51223f4d460e6e77119439e78f2b592cafade",
"sha256:db78cc5c03b446a43413165aa873e2f408e9fd5ddb45533e7bd3b638bace867c",
"sha256:dbc5029c61f9ebb2d4c247f13584a0ef0e8e49abb13e56460310821aca3ffcaf",
"sha256:ddb319f869d497ef2d3d56319360b61284a9a1d8b3de3bc936748698acfda6be",
"sha256:e0e4fdeae6c0a9d886749780ec5dcf469e98f27b312efa93008d03eaa2426fd5",
"sha256:e4c5e7edf1e7bcbde3b52058f171a411e2a24a081b3e951d685dfea4c3c383d5",
"sha256:e71c9dba78671d38a549e3b2d52514f50e199f9d7e18ed9b0180adeef0d04130",
"sha256:e997d22e0d1e08c8752f61675a75d93659f7aa4dbeaee54207f8d877817b4a0c",
"sha256:efa5834ba5e6c70b22afdca3894097e5a592d8d483c976359654ba990477799a",
"sha256:f2d951002b11962b26afb31f758c18ad39771f287b100fa5adb1d09a47eaaf5b",
"sha256:f3f96f57cea35ba19fd23a20b38fa0dfa3d87d582507129b8c8e314aa298f59b",
"sha256:f738051052abc95dc17f9a4c92044294a263fb7f762efdb13e528d419005c0e4",
"sha256:f76784355060999c36fa807b59faecb38f5769ae58283d00270835773f95e35b",
"sha256:f92462ea3888c99439f58f7561ecd5dd4cf8b8b1b259ccf5376667b8c46ee747",
"sha256:fefd18b29f3b84a0cdea1d86340219d9871c3b0673a38e722a73a2c39591eaa7"
],
"version": "==3.7.1"
"version": "==3.6.0"
},
"bitstring": {
"hashes": [
@@ -173,93 +173,76 @@
},
"cffi": {
"hashes": [
"sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb",
"sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b",
"sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f",
"sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9",
"sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44",
"sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2",
"sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c",
"sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75",
"sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65",
"sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e",
"sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a",
"sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e",
"sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25",
"sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a",
"sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe",
"sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b",
"sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91",
"sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592",
"sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187",
"sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c",
"sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1",
"sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94",
"sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba",
"sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb",
"sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165",
"sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529",
"sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca",
"sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c",
"sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6",
"sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c",
"sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0",
"sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743",
"sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63",
"sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5",
"sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5",
"sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4",
"sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d",
"sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b",
"sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93",
"sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205",
"sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27",
"sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512",
"sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d",
"sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c",
"sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037",
"sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26",
"sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322",
"sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb",
"sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c",
"sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8",
"sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4",
"sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414",
"sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9",
"sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664",
"sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9",
"sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775",
"sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739",
"sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc",
"sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062",
"sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe",
"sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9",
"sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92",
"sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5",
"sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13",
"sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d",
"sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26",
"sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f",
"sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495",
"sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b",
"sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6",
"sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c",
"sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef",
"sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5",
"sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18",
"sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad",
"sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3",
"sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7",
"sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5",
"sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534",
"sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49",
"sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2",
"sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5",
"sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453",
"sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"
"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": "==2.0.0"
"version": "==1.17.1"
},
"click": {
"hashes": [
@@ -271,46 +254,46 @@
},
"cryptography": {
"hashes": [
"sha256:06ce84dc14df0bf6ea84666f958e6080cdb6fe1231be2a51f3fc1267d9f3fb34",
"sha256:16ede8a4f7929b4b7ff3642eba2bf79aa1d71f24ab6ee443935c0d269b6bc513",
"sha256:18fcf70f243fe07252dcb1b268a687f2358025ce32f9f88028ca5c364b123ef5",
"sha256:1993a1bb7e4eccfb922b6cd414f072e08ff5816702a0bdb8941c247a6b1b287c",
"sha256:1f3d56f73595376f4244646dd5c5870c14c196949807be39e79e7bd9bac3da63",
"sha256:258e0dff86d1d891169b5af222d362468a9570e2532923088658aa866eb11130",
"sha256:2f641b64acc00811da98df63df7d59fd4706c0df449da71cb7ac39a0732b40ae",
"sha256:3808e6b2e5f0b46d981c24d79648e5c25c35e59902ea4391a0dcb3e667bf7443",
"sha256:3994c809c17fc570c2af12c9b840d7cea85a9fd3e5c0e0491f4fa3c029216d59",
"sha256:3be4f21c6245930688bd9e162829480de027f8bf962ede33d4f8ba7d67a00cee",
"sha256:465ccac9d70115cd4de7186e60cfe989de73f7bb23e8a7aa45af18f7412e75bf",
"sha256:48c41a44ef8b8c2e80ca4527ee81daa4c527df3ecbc9423c41a420a9559d0e27",
"sha256:4a862753b36620af6fc54209264f92c716367f2f0ff4624952276a6bbd18cbde",
"sha256:4b1654dfc64ea479c242508eb8c724044f1e964a47d1d1cacc5132292d851971",
"sha256:4bd3e5c4b9682bc112d634f2c6ccc6736ed3635fc3319ac2bb11d768cc5a00d8",
"sha256:577470e39e60a6cd7780793202e63536026d9b8641de011ed9d8174da9ca5339",
"sha256:67285f8a611b0ebc0857ced2081e30302909f571a46bfa7a3cc0ad303fe015c6",
"sha256:7285a89df4900ed3bfaad5679b1e668cb4b38a8de1ccbfc84b05f34512da0a90",
"sha256:81823935e2f8d476707e85a78a405953a03ef7b7b4f55f93f7c2d9680e5e0691",
"sha256:8978132287a9d3ad6b54fcd1e08548033cc09dc6aacacb6c004c73c3eb5d3ac3",
"sha256:a20e442e917889d1a6b3c570c9e3fa2fdc398c20868abcea268ea33c024c4083",
"sha256:a24ee598d10befaec178efdff6054bc4d7e883f615bfbcd08126a0f4931c83a6",
"sha256:b04f85ac3a90c227b6e5890acb0edbaf3140938dbecf07bff618bf3638578cf1",
"sha256:b6a0e535baec27b528cb07a119f321ac024592388c5681a5ced167ae98e9fff3",
"sha256:bef32a5e327bd8e5af915d3416ffefdbe65ed975b646b3805be81b23580b57b8",
"sha256:bfb4c801f65dd61cedfc61a83732327fafbac55a47282e6f26f073ca7a41c3b2",
"sha256:c13b1e3afd29a5b3b2656257f14669ca8fa8d7956d509926f0b130b600b50ab7",
"sha256:c987dad82e8c65ebc985f5dae5e74a3beda9d0a2a4daf8a1115f3772b59e5141",
"sha256:ce7a453385e4c4693985b4a4a3533e041558851eae061a58a5405363b098fcd3",
"sha256:d0c5c6bac22b177bf8da7435d9d27a6834ee130309749d162b26c3105c0795a9",
"sha256:d97cf502abe2ab9eff8bd5e4aca274da8d06dd3ef08b759a8d6143f4ad65d4b4",
"sha256:dad43797959a74103cb59c5dac71409f9c27d34c8a05921341fb64ea8ccb1dd4",
"sha256:dd342f085542f6eb894ca00ef70236ea46070c8a13824c6bde0dfdcd36065b9b",
"sha256:de58755d723e86175756f463f2f0bddd45cc36fbd62601228a3f8761c9f58252",
"sha256:f3df7b3d0f91b88b2106031fd995802a2e9ae13e02c36c1fc075b43f420f3a17",
"sha256:f5414a788ecc6ee6bc58560e85ca624258a55ca434884445440a810796ea0e0b",
"sha256:fa26fa54c0a9384c27fcdc905a2fb7d60ac6e47d14bc2692145f2b3b1e2cfdbd"
"sha256:00e8724bdad672d75e6f069b27970883179bd472cd24a63f6e620ca7e41cc0c5",
"sha256:048e7ad9e08cf4c0ab07ff7f36cc3115924e22e2266e034450a890d9e312dd74",
"sha256:0d9ef57b6768d9fa58e92f4947cea96ade1233c0e236db22ba44748ffedca394",
"sha256:18f878a34b90d688982e43f4b700408b478102dd58b3e39de21b5ebf6509c301",
"sha256:1b7fa6a1c1188c7ee32e47590d16a5a0646270921f8020efc9a511648e1b2e08",
"sha256:20ae4906a13716139d6d762ceb3e0e7e110f7955f3bc3876e3a07f5daadec5f3",
"sha256:20d15aed3ee522faac1a39fbfdfee25d17b1284bafd808e1640a74846d7c4d1b",
"sha256:2384f2ab18d9be88a6e4f8972923405e2dbb8d3e16c6b43f15ca491d7831bd18",
"sha256:275ba5cc0d9e320cd70f8e7b96d9e59903c815ca579ab96c1e37278d231fc402",
"sha256:2dac5ec199038b8e131365e2324c03d20e97fe214af051d20c49db129844e8b3",
"sha256:31a2b9a10530a1cb04ffd6aa1cd4d3be9ed49f7d77a4dafe198f3b382f41545c",
"sha256:3436128a60a5e5490603ab2adbabc8763613f638513ffa7d311c900a8349a2a0",
"sha256:3b5bf5267e98661b9b888a9250d05b063220dfa917a8203744454573c7eb79db",
"sha256:3de77e4df42ac8d4e4d6cdb342d989803ad37707cf8f3fbf7b088c9cbdd46427",
"sha256:44647c5d796f5fc042bbc6d61307d04bf29bccb74d188f18051b635f20a9c75f",
"sha256:550ae02148206beb722cfe4ef0933f9352bab26b087af00e48fdfb9ade35c5b3",
"sha256:599c8d7df950aa68baa7e98f7b73f4f414c9f02d0e8104a30c0182a07732638b",
"sha256:5b64e668fc3528e77efa51ca70fadcd6610e8ab231e3e06ae2bab3b31c2b8ed9",
"sha256:5bd6020c80c5b2b2242d6c48487d7b85700f5e0038e67b29d706f98440d66eb5",
"sha256:5c966c732cf6e4a276ce83b6e4c729edda2df6929083a952cc7da973c539c719",
"sha256:629127cfdcdc6806dfe234734d7cb8ac54edaf572148274fa377a7d3405b0043",
"sha256:705bb7c7ecc3d79a50f236adda12ca331c8e7ecfbea51edd931ce5a7a7c4f012",
"sha256:780c40fb751c7d2b0c6786ceee6b6f871e86e8718a8ff4bc35073ac353c7cd02",
"sha256:7a3085d1b319d35296176af31c90338eeb2ddac8104661df79f80e1d9787b8b2",
"sha256:826b46dae41a1155a0c0e66fafba43d0ede1dc16570b95e40c4d83bfcf0a451d",
"sha256:833dc32dfc1e39b7376a87b9a6a4288a10aae234631268486558920029b086ec",
"sha256:cc4d66f5dc4dc37b89cfef1bd5044387f7a1f6f0abb490815628501909332d5d",
"sha256:d063341378d7ee9c91f9d23b431a3502fc8bfacd54ef0a27baa72a0843b29159",
"sha256:e2a21a8eda2d86bb604934b6b37691585bd095c1f788530c1fcefc53a82b3453",
"sha256:e40b80ecf35ec265c452eea0ba94c9587ca763e739b8e559c128d23bff7ebbbf",
"sha256:e5b3dda1b00fb41da3af4c5ef3f922a200e33ee5ba0f0bc9ecf0b0c173958385",
"sha256:ea3c42f2016a5bbf71825537c2ad753f2870191134933196bee408aac397b3d9",
"sha256:eccddbd986e43014263eda489abbddfbc287af5cddfd690477993dbb31e31016",
"sha256:ee411a1b977f40bd075392c80c10b58025ee5c6b47a822a33c1198598a7a5f05",
"sha256:f4028f29a9f38a2025abedb2e409973709c660d44319c61762202206ed577c42",
"sha256:f68f833a9d445cc49f01097d95c83a850795921b3f7cc6488731e69bde3288da",
"sha256:fc022c1fa5acff6def2fc6d7819bbbd31ccddfe67d075331a65d9cfb28a20983"
],
"markers": "python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1'",
"version": "==45.0.7"
"version": "==45.0.6"
},
"esptool": {
"hashes": [
@@ -320,14 +303,6 @@
"markers": "python_version >= '3.10'",
"version": "==5.0.2"
},
"h11": {
"hashes": [
"sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1",
"sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"
],
"markers": "python_version >= '3.8'",
"version": "==0.16.0"
},
"idna": {
"hashes": [
"sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9",
@@ -345,11 +320,11 @@
},
"markdown-it-py": {
"hashes": [
"sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147",
"sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3"
"sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1",
"sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"
],
"markers": "python_version >= '3.10'",
"version": "==4.0.0"
"markers": "python_version >= '3.8'",
"version": "==3.0.0"
},
"mdurl": {
"hashes": [
@@ -370,19 +345,19 @@
},
"platformdirs": {
"hashes": [
"sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85",
"sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf"
"sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc",
"sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"
],
"markers": "python_version >= '3.9'",
"version": "==4.4.0"
"version": "==4.3.8"
},
"pycparser": {
"hashes": [
"sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2",
"sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"
"sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6",
"sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"
],
"markers": "implementation_name != 'PyPy'",
"version": "==2.23"
"markers": "python_version >= '3.8'",
"version": "==2.22"
},
"pygments": {
"hashes": [
@@ -492,20 +467,11 @@
},
"typing-extensions": {
"hashes": [
"sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466",
"sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"
"sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36",
"sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76"
],
"markers": "python_version < '3.13'",
"version": "==4.15.0"
},
"uvicorn": {
"hashes": [
"sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a",
"sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01"
],
"index": "pypi",
"markers": "python_version >= '3.9'",
"version": "==0.35.0"
"version": "==4.14.1"
},
"watchfiles": {
"hashes": [

View File

@@ -1,58 +0,0 @@
#!/usr/bin/env python3
"""
LED Bar Configuration Script
Updates the settings.json file for each LED bar with its unique name
"""
import json
import os
# LED Bar names/IDs
LED_BAR_NAMES = ["100", "101", "102", "103", "104", "105", "106", "107"]
def update_bar_settings(bar_name, settings_file="settings.json"):
"""Update the settings.json file with the bar name"""
if not os.path.exists(settings_file):
print(f"Error: {settings_file} not found")
return False
# Read current settings
with open(settings_file, 'r') as f:
settings = json.load(f)
# Update the name
settings["name"] = bar_name
# Write back to file
with open(settings_file, 'w') as f:
json.dump(settings, f, indent=4)
print(f"Updated {settings_file} with name: {bar_name}")
return True
def main():
print("LED Bar Configuration Script")
print("=" * 40)
print("Available bar names:", LED_BAR_NAMES)
print()
while True:
print("Enter bar name to configure (or 'quit' to exit):")
bar_name = input("> ").strip()
if bar_name.lower() == 'quit':
break
if bar_name not in LED_BAR_NAMES:
print(f"Invalid bar name. Must be one of: {LED_BAR_NAMES}")
continue
if update_bar_settings(bar_name):
print(f"Successfully configured LED bar as '{bar_name}'")
else:
print("Failed to update settings")
print()
if __name__ == "__main__":
main()

View File

@@ -1,165 +0,0 @@
#!/usr/bin/env python3
"""
Settings editor - copy from device, edit locally, upload back
Run with: python3 edit_settings.py
"""
import json
import subprocess
import os
import tempfile
def copy_settings_from_device():
"""Copy settings.json from the MicroPython device"""
print("Copying settings from device...")
try:
# Use mpremote to copy settings.json to local temp file
result = subprocess.run(['mpremote', 'cp', ':/settings.json', 'settings.json'],
capture_output=True, text=True)
if result.returncode == 0:
print("Settings copied successfully")
return True
else:
print(f"Failed to copy settings: {result.stderr}")
return False
except Exception as e:
print(f"Error copying settings: {e}")
return False
def load_local_settings():
"""Load settings from local file"""
try:
with open('settings.json', 'r') as f:
return json.load(f)
except FileNotFoundError:
print("No local settings file found")
return {}
except Exception as e:
print(f"Error loading settings: {e}")
return {}
def save_local_settings(settings):
"""Save settings to local file"""
try:
with open('settings.json', 'w') as f:
json.dump(settings, f, indent=2)
print("Settings saved locally")
return True
except Exception as e:
print(f"Error saving settings: {e}")
return False
def upload_settings_to_device():
"""Upload settings.json to the MicroPython device"""
print("Uploading settings to device...")
try:
result = subprocess.run(['mpremote', 'cp', 'settings.json', ':/settings.json'],
capture_output=True, text=True)
if result.returncode == 0:
print("Settings uploaded successfully")
return True
else:
print(f"Failed to upload settings: {result.stderr}")
return False
except Exception as e:
print(f"Error uploading settings: {e}")
return False
def edit_settings_interactive(settings):
"""Interactive settings editor"""
print("\nLED Bar Settings Editor")
print("=======================")
# Display current settings
current_pin = settings.get("led_pin", 10)
current_leds = settings.get("num_leds", 119)
current_name = settings.get("name", "104")
current_color_order = settings.get("color_order", "rgb")
print(f"\nCurrent settings:")
print(f" 1. LED Pin: {current_pin}")
print(f" 2. Number of LEDs: {current_leds}")
print(f" 3. Device Name: {current_name}")
print(f" 4. Color Order: {current_color_order}")
print(f"\nEnter new values (press Enter to keep current):")
# LED Pin
new_pin = input(f"LED Pin [{current_pin}]: ").strip()
if new_pin:
try:
settings["led_pin"] = int(new_pin)
except ValueError:
print("Invalid pin number, keeping current value.")
# Number of LEDs
new_leds = input(f"Number of LEDs [{current_leds}]: ").strip()
if new_leds:
try:
settings["num_leds"] = int(new_leds)
except ValueError:
print("Invalid LED count, keeping current value.")
# Device Name
new_name = input(f"Device Name [{current_name}]: ").strip()
if new_name:
settings["name"] = new_name
# Color Order
print(f"Color Order options: rgb, rbg")
new_color_order = input(f"Color Order [{current_color_order}]: ").strip().lower()
if new_color_order in ['rgb', 'rbg']:
settings["color_order"] = new_color_order
elif new_color_order:
print("Invalid color order, keeping current value.")
return settings
def main():
print("LED Bar Settings Editor")
print("=======================")
# Step 1: Copy settings from device
if not copy_settings_from_device():
print("Creating default settings...")
default_settings = {
"led_pin": 10,
"num_leds": 119,
"color_order": "rgb",
"name": "104"
}
save_local_settings(default_settings)
# Step 2: Load and edit settings
settings = load_local_settings()
if not settings:
print("No settings to edit")
return
# Step 3: Interactive editing
edited_settings = edit_settings_interactive(settings.copy())
# Step 4: Save locally
if edited_settings != settings:
save_local_settings(edited_settings)
# Step 5: Upload to device
upload_choice = input("\nUpload settings to device? (y/n): ").strip().lower()
if upload_choice in ['y', 'yes']:
if upload_settings_to_device():
print("\nSettings updated successfully!")
print("Restart the device to apply changes.")
else:
print("\nFailed to upload settings.")
else:
print("\nSettings saved locally but not uploaded.")
else:
print("\nNo changes made.")
if __name__ == "__main__":
main()

View File

@@ -1,2 +0,0 @@
from microdot.microdot import Microdot, Request, Response, abort, redirect, \
send_file # noqa: F401

View File

@@ -1,8 +0,0 @@
try:
from functools import wraps
except ImportError: # pragma: no cover
# MicroPython does not currently implement functools.wraps
def wraps(wrapped):
def _(wrapper):
return wrapper
return _

File diff suppressed because it is too large Load Diff

View File

@@ -1,70 +0,0 @@
from utemplate import recompile
_loader = None
class Template:
"""A template object.
:param template: The filename of the template to render, relative to the
configured template directory.
"""
@classmethod
def initialize(cls, template_dir='templates',
loader_class=recompile.Loader):
"""Initialize the templating subsystem.
:param template_dir: the directory where templates are stored. This
argument is optional. The default is to load
templates from a *templates* subdirectory.
:param loader_class: the ``utemplate.Loader`` class to use when loading
templates. This argument is optional. The default
is the ``recompile.Loader`` class, which
automatically recompiles templates when they
change.
"""
global _loader
_loader = loader_class(None, template_dir)
def __init__(self, template):
if _loader is None: # pragma: no cover
self.initialize()
#: The name of the template
self.name = template
self.template = _loader.load(template)
def generate(self, *args, **kwargs):
"""Return a generator that renders the template in chunks, with the
given arguments."""
return self.template(*args, **kwargs)
def render(self, *args, **kwargs):
"""Render the template with the given arguments and return it as a
string."""
return ''.join(self.generate(*args, **kwargs))
def generate_async(self, *args, **kwargs):
"""Return an asynchronous generator that renders the template in
chunks, using the given arguments."""
class sync_to_async_iter():
def __init__(self, iter):
self.iter = iter
def __aiter__(self):
return self
async def __anext__(self):
try:
return next(self.iter)
except StopIteration:
raise StopAsyncIteration
return sync_to_async_iter(self.generate(*args, **kwargs))
async def render_async(self, *args, **kwargs):
"""Render the template with the given arguments asynchronously and
return it as a string."""
response = ''
async for chunk in self.generate_async(*args, **kwargs):
response += chunk
return response

View File

@@ -1,231 +0,0 @@
import binascii
import hashlib
from microdot import Request, Response
from microdot.microdot import MUTED_SOCKET_ERRORS, print_exception
from microdot.helpers import wraps
class WebSocketError(Exception):
"""Exception raised when an error occurs in a WebSocket connection."""
pass
class WebSocket:
"""A WebSocket connection object.
An instance of this class is sent to handler functions to manage the
WebSocket connection.
"""
CONT = 0
TEXT = 1
BINARY = 2
CLOSE = 8
PING = 9
PONG = 10
#: Specify the maximum message size that can be received when calling the
#: ``receive()`` method. Messages with payloads that are larger than this
#: size will be rejected and the connection closed. Set to 0 to disable
#: the size check (be aware of potential security issues if you do this),
#: or to -1 to use the value set in
#: ``Request.max_body_length``. The default is -1.
#:
#: Example::
#:
#: WebSocket.max_message_length = 4 * 1024 # up to 4KB messages
max_message_length = -1
def __init__(self, request):
self.request = request
self.closed = False
async def handshake(self):
response = self._handshake_response()
await self.request.sock[1].awrite(
b'HTTP/1.1 101 Switching Protocols\r\n')
await self.request.sock[1].awrite(b'Upgrade: websocket\r\n')
await self.request.sock[1].awrite(b'Connection: Upgrade\r\n')
await self.request.sock[1].awrite(
b'Sec-WebSocket-Accept: ' + response + b'\r\n\r\n')
async def receive(self):
"""Receive a message from the client."""
while True:
opcode, payload = await self._read_frame()
send_opcode, data = self._process_websocket_frame(opcode, payload)
if send_opcode: # pragma: no cover
await self.send(data, send_opcode)
elif data: # pragma: no branch
return data
async def send(self, data, opcode=None):
"""Send a message to the client.
:param data: the data to send, given as a string or bytes.
:param opcode: a custom frame opcode to use. If not given, the opcode
is ``TEXT`` or ``BINARY`` depending on the type of the
data.
"""
frame = self._encode_websocket_frame(
opcode or (self.TEXT if isinstance(data, str) else self.BINARY),
data)
await self.request.sock[1].awrite(frame)
async def close(self):
"""Close the websocket connection."""
if not self.closed: # pragma: no cover
self.closed = True
await self.send(b'', self.CLOSE)
def _handshake_response(self):
connection = False
upgrade = False
websocket_key = None
for header, value in self.request.headers.items():
h = header.lower()
if h == 'connection':
connection = True
if 'upgrade' not in value.lower():
return self.request.app.abort(400)
elif h == 'upgrade':
upgrade = True
if not value.lower() == 'websocket':
return self.request.app.abort(400)
elif h == 'sec-websocket-key':
websocket_key = value
if not connection or not upgrade or not websocket_key:
return self.request.app.abort(400)
d = hashlib.sha1(websocket_key.encode())
d.update(b'258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
return binascii.b2a_base64(d.digest())[:-1]
@classmethod
def _parse_frame_header(cls, header):
fin = header[0] & 0x80
opcode = header[0] & 0x0f
if fin == 0 or opcode == cls.CONT: # pragma: no cover
raise WebSocketError('Continuation frames not supported')
has_mask = header[1] & 0x80
length = header[1] & 0x7f
if length == 126:
length = -2
elif length == 127:
length = -8
return fin, opcode, has_mask, length
def _process_websocket_frame(self, opcode, payload):
if opcode == self.TEXT:
payload = payload.decode()
elif opcode == self.BINARY:
pass
elif opcode == self.CLOSE:
raise WebSocketError('Websocket connection closed')
elif opcode == self.PING:
return self.PONG, payload
elif opcode == self.PONG: # pragma: no branch
return None, None
return None, payload
@classmethod
def _encode_websocket_frame(cls, opcode, payload):
frame = bytearray()
frame.append(0x80 | opcode)
if opcode == cls.TEXT:
payload = payload.encode()
if len(payload) < 126:
frame.append(len(payload))
elif len(payload) < (1 << 16):
frame.append(126)
frame.extend(len(payload).to_bytes(2, 'big'))
else:
frame.append(127)
frame.extend(len(payload).to_bytes(8, 'big'))
frame.extend(payload)
return frame
async def _read_frame(self):
header = await self.request.sock[0].read(2)
if len(header) != 2: # pragma: no cover
raise WebSocketError('Websocket connection closed')
fin, opcode, has_mask, length = self._parse_frame_header(header)
if length == -2:
length = await self.request.sock[0].read(2)
length = int.from_bytes(length, 'big')
elif length == -8:
length = await self.request.sock[0].read(8)
length = int.from_bytes(length, 'big')
max_allowed_length = Request.max_body_length \
if self.max_message_length == -1 else self.max_message_length
if length > max_allowed_length:
raise WebSocketError('Message too large')
if has_mask: # pragma: no cover
mask = await self.request.sock[0].read(4)
payload = await self.request.sock[0].read(length)
if has_mask: # pragma: no cover
payload = bytes(x ^ mask[i % 4] for i, x in enumerate(payload))
return opcode, payload
async def websocket_upgrade(request):
"""Upgrade a request handler to a websocket connection.
This function can be called directly inside a route function to process a
WebSocket upgrade handshake, for example after the user's credentials are
verified. The function returns the websocket object::
@app.route('/echo')
async def echo(request):
if not authenticate_user(request):
abort(401)
ws = await websocket_upgrade(request)
while True:
message = await ws.receive()
await ws.send(message)
"""
ws = WebSocket(request)
await ws.handshake()
@request.after_request
async def after_request(request, response):
return Response.already_handled
return ws
def websocket_wrapper(f, upgrade_function):
@wraps(f)
async def wrapper(request, *args, **kwargs):
ws = await upgrade_function(request)
try:
await f(request, ws, *args, **kwargs)
except OSError as exc:
if exc.errno not in MUTED_SOCKET_ERRORS: # pragma: no cover
raise
except WebSocketError:
pass
except Exception as exc:
print_exception(exc)
finally: # pragma: no cover
try:
await ws.close()
except Exception:
pass
return Response.already_handled
return wrapper
def with_websocket(f):
"""Decorator to make a route a WebSocket endpoint.
This decorator is used to define a route that accepts websocket
connections. The route then receives a websocket object as a second
argument that it can use to send and receive messages::
@app.route('/echo')
@with_websocket
async def echo(request, ws):
while True:
message = await ws.receive()
await ws.send(message)
"""
return websocket_wrapper(f, websocket_upgrade)

View File

@@ -1,14 +0,0 @@
class Loader:
def __init__(self, pkg, dir):
if dir == ".":
dir = ""
else:
dir = dir.replace("/", ".") + "."
if pkg and pkg != "__main__":
dir = pkg + "." + dir
self.p = dir
def load(self, name):
name = name.replace(".", "_")
return __import__(self.p + name, None, None, (name,)).render

View File

@@ -1,21 +0,0 @@
# (c) 2014-2020 Paul Sokolovsky. MIT license.
try:
from uos import stat, remove
except:
from os import stat, remove
from . import source
class Loader(source.Loader):
def load(self, name):
o_path = self.pkg_path + self.compiled_path(name)
i_path = self.pkg_path + self.dir + "/" + name
try:
o_stat = stat(o_path)
i_stat = stat(i_path)
if i_stat[8] > o_stat[8]:
# input file is newer, remove output to force recompile
remove(o_path)
finally:
return super().load(name)

View File

@@ -1,188 +0,0 @@
# (c) 2014-2019 Paul Sokolovsky. MIT license.
from . import compiled
class Compiler:
START_CHAR = "{"
STMNT = "%"
STMNT_END = "%}"
EXPR = "{"
EXPR_END = "}}"
def __init__(self, file_in, file_out, indent=0, seq=0, loader=None):
self.file_in = file_in
self.file_out = file_out
self.loader = loader
self.seq = seq
self._indent = indent
self.stack = []
self.in_literal = False
self.flushed_header = False
self.args = "*a, **d"
def indent(self, adjust=0):
if not self.flushed_header:
self.flushed_header = True
self.indent()
self.file_out.write("def render%s(%s):\n" % (str(self.seq) if self.seq else "", self.args))
self.stack.append("def")
self.file_out.write(" " * (len(self.stack) + self._indent + adjust))
def literal(self, s):
if not s:
return
if not self.in_literal:
self.indent()
self.file_out.write('yield """')
self.in_literal = True
self.file_out.write(s.replace('"', '\\"'))
def close_literal(self):
if self.in_literal:
self.file_out.write('"""\n')
self.in_literal = False
def render_expr(self, e):
self.indent()
self.file_out.write('yield str(' + e + ')\n')
def parse_statement(self, stmt):
tokens = stmt.split(None, 1)
if tokens[0] == "args":
if len(tokens) > 1:
self.args = tokens[1]
else:
self.args = ""
elif tokens[0] == "set":
self.indent()
self.file_out.write(stmt[3:].strip() + "\n")
elif tokens[0] == "include":
if not self.flushed_header:
# If there was no other output, we still need a header now
self.indent()
tokens = tokens[1].split(None, 1)
args = ""
if len(tokens) > 1:
args = tokens[1]
if tokens[0][0] == "{":
self.indent()
# "1" as fromlist param is uPy hack
self.file_out.write('_ = __import__(%s.replace(".", "_"), None, None, 1)\n' % tokens[0][2:-2])
self.indent()
self.file_out.write("yield from _.render(%s)\n" % args)
return
with self.loader.input_open(tokens[0][1:-1]) as inc:
self.seq += 1
c = Compiler(inc, self.file_out, len(self.stack) + self._indent, self.seq)
inc_id = self.seq
self.seq = c.compile()
self.indent()
self.file_out.write("yield from render%d(%s)\n" % (inc_id, args))
elif len(tokens) > 1:
if tokens[0] == "elif":
assert self.stack[-1] == "if"
self.indent(-1)
self.file_out.write(stmt + ":\n")
else:
self.indent()
self.file_out.write(stmt + ":\n")
self.stack.append(tokens[0])
else:
if stmt.startswith("end"):
assert self.stack[-1] == stmt[3:]
self.stack.pop(-1)
elif stmt == "else":
assert self.stack[-1] == "if"
self.indent(-1)
self.file_out.write("else:\n")
else:
assert False
def parse_line(self, l):
while l:
start = l.find(self.START_CHAR)
if start == -1:
self.literal(l)
return
self.literal(l[:start])
self.close_literal()
sel = l[start + 1]
#print("*%s=%s=" % (sel, EXPR))
if sel == self.STMNT:
end = l.find(self.STMNT_END)
assert end > 0
stmt = l[start + len(self.START_CHAR + self.STMNT):end].strip()
self.parse_statement(stmt)
end += len(self.STMNT_END)
l = l[end:]
if not self.in_literal and l == "\n":
break
elif sel == self.EXPR:
# print("EXPR")
end = l.find(self.EXPR_END)
assert end > 0
expr = l[start + len(self.START_CHAR + self.EXPR):end].strip()
self.render_expr(expr)
end += len(self.EXPR_END)
l = l[end:]
else:
self.literal(l[start])
l = l[start + 1:]
def header(self):
self.file_out.write("# Autogenerated file\n")
def compile(self):
self.header()
for l in self.file_in:
self.parse_line(l)
self.close_literal()
return self.seq
class Loader(compiled.Loader):
def __init__(self, pkg, dir):
super().__init__(pkg, dir)
self.dir = dir
if pkg == "__main__":
# if pkg isn't really a package, don't bother to use it
# it means we're running from "filesystem directory", not
# from a package.
pkg = None
self.pkg_path = ""
if pkg:
p = __import__(pkg)
if isinstance(p.__path__, str):
# uPy
self.pkg_path = p.__path__
else:
# CPy
self.pkg_path = p.__path__[0]
self.pkg_path += "/"
def input_open(self, template):
path = self.pkg_path + self.dir + "/" + template
return open(path)
def compiled_path(self, template):
return self.dir + "/" + template.replace(".", "_") + ".py"
def load(self, name):
try:
return super().load(name)
except (OSError, ImportError):
pass
compiled_path = self.pkg_path + self.compiled_path(name)
f_in = self.input_open(name)
f_out = open(compiled_path, "w")
c = Compiler(f_in, f_out, loader=self)
c.compile()
f_in.close()
f_out.close()
return super().load(name)

View File

@@ -1,431 +0,0 @@
import utime
import random
from patterns_base import PatternBase # Import PatternBase
class Patterns(PatternBase): # Inherit from PatternBase
def __init__(self, pin, num_leds, color1=(0,0,0), color2=(0,0,0), brightness=127, selected="rainbow_cycle", delay=100):
super().__init__(pin, num_leds, color1, color2, brightness, selected, delay) # Call parent constructor
# Pattern-specific initializations
self.on_width = 1 # Default on width
self.off_width = 2 # Default off width (so total segment is 3, matching original behavior)
self.n1 = 0 # Default start of fill range
self.n2 = self.num_leds - 1 # Default end of fill range
self.oneshot = False # New: One-shot flag for patterns like fill_range
self.patterns = {
"off": self.off,
"on" : self.on,
"color_wipe": self.color_wipe,
"rainbow_cycle": self.rainbow_cycle,
"theater_chase": self.theater_chase,
"blink": self.blink,
"color_transition": self.color_transition, # Added new pattern
"flicker": self.flicker,
"scanner": self.scanner, # New: Single direction scanner
"bidirectional_scanner": self.bidirectional_scanner, # New: Bidirectional scanner
"fill_range": self.fill_range, # New: Fill from n1 to n2
"n_chase": self.n_chase, # New: N1 on, N2 off repeating chase
"alternating": self.alternating, # New: N1 on/off, N2 off/on alternating chase
"external": None,
"pulse": self.pulse
}
# Beat-related functionality removed
# self.selected is already initialized in PatternBase, but we need to ensure it uses our patterns dict
# self.selected = selected # Handled by PatternBase
# Ensure colors list always starts with at least two for robust transition handling
# self.colors handled by PatternBase
# Transition attributes handled by PatternBase
# Scanner attributes handled by PatternBase
# self.run handled by PatternBase
def set_on_width(self, on_width):
self.on_width = on_width
def set_off_width(self, off_width):
self.off_width = off_width
def set_on_off_width(self, on_width, off_width):
self.on_width = on_width
self.off_width = off_width
self.sync()
def set_fill_range(self, n1, n2):
self.n1 = n1
self.n2 = n2
self.sync()
def set_oneshot(self, oneshot_value):
self.oneshot = oneshot_value
if self.oneshot: # Reset pattern step if enabling one-shot
self.pattern_step = 0
self.sync()
def select(self, pattern):
if pattern in self.patterns:
super().select(pattern) # Use parent select to set self.selected and self.transition_step
self.run = True # Set run flag
if pattern == "color_transition":
if len(self.colors) < 2:
print("Warning: 'color_transition' requires at least two colors. Switching to 'on'.")
self.selected = "on" # Fallback if not enough colors
self.sync() # Re-sync for the new pattern
else:
self.transition_step = 0
self.current_color_idx = 0 # Start from the first color in the list
self.current_color = self.colors[self.current_color_idx]
self.hold_start_time = utime.ticks_ms() # Reset hold timer
self.transition_duration = self.delay * 50 # Initialize transition duration
self.hold_duration = self.delay * 10 # Initialize hold duration
return True
return False
def off(self):
self.fill((0, 0, 0))
return self.delay
def on(self):
self.fill(self.apply_brightness(self.colors[0]))
return self.delay
def color_wipe(self):
color = self.apply_brightness(self.colors[0])
current_time = utime.ticks_ms()
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
return self.delay
def rainbow_cycle(self):
current_time = utime.ticks_ms()
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)
# Rainbow travels along the length - each LED position gets a different color
for i in range(self.num_leds):
# Map LED position along strip to rainbow color
# Full 256 color range distributed along the strip length
rc_index = ((i * 256 // self.num_leds) + self.pattern_step) % 256
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
return max(1, int(self.delay // 5))
def theater_chase(self):
current_time = utime.ticks_ms()
segment_length = self.on_width + self.off_width
for i in range(self.num_leds):
if (i + self.pattern_step) % segment_length < self.on_width:
self.n[i] = self.apply_brightness(self.colors[0])
else:
self.n[i] = (0, 0, 0)
self.n.write()
self.pattern_step = (self.pattern_step + 1) % segment_length
self.last_update = current_time
return self.delay
def blink(self):
current_time = utime.ticks_ms()
if self.pattern_step % 2 == 0:
self.fill(self.apply_brightness(self.colors[0]))
else:
self.fill((0, 0, 0))
self.pattern_step = (self.pattern_step + 1) % 2
self.last_update = current_time
return self.delay
def color_transition(self):
current_time = utime.ticks_ms()
# Check for hold duration first
if utime.ticks_diff(current_time, self.hold_start_time) < self.hold_duration:
# Still in hold phase, just display the current solid color
self.fill(self.apply_brightness(self.current_color))
self.last_update = current_time # Keep updating last_update to avoid skipping frames
return self.delay
# If hold duration is over, proceed with transition
if utime.ticks_diff(current_time, self.last_update) >= self.delay:
num_colors = len(self.colors)
if num_colors < 2:
# Should not happen if select handles it, but as a safeguard
self.select("on")
return self.delay
from_color = self.colors[self.current_color_idx]
to_color_idx = (self.current_color_idx + 1) % num_colors
to_color = self.colors[to_color_idx]
# Calculate interpolation factor (0.0 to 1.0)
# transition_step goes from 0 to transition_duration - 1
if self.transition_duration > 0:
interp_factor = self.transition_step / self.transition_duration
else:
interp_factor = 1.0 # Immediately transition if duration is zero
# Interpolate each color component
r = int(from_color[0] + (to_color[0] - from_color[0]) * interp_factor)
g = int(from_color[1] + (to_color[1] - from_color[1]) * interp_factor)
b = int(from_color[2] + (to_color[2] - from_color[2]) * interp_factor)
self.current_color = (r, g, b)
self.fill(self.apply_brightness(self.current_color))
self.transition_step += self.delay # Advance the transition step by the delay
if self.transition_step >= self.transition_duration:
# Transition complete, move to the next color and reset for hold phase
self.current_color_idx = to_color_idx
self.current_color = self.colors[self.current_color_idx] # Ensure current_color is the exact target color
self.transition_step = 0 # Reset transition progress
self.hold_start_time = current_time # Start hold phase for the new color
self.last_update = current_time
return self.delay
def flicker(self):
current_time = utime.ticks_ms()
base_color = self.colors[0]
# Increase the range for flicker_brightness_offset
# Changed from self.brightness // 4 to self.brightness // 2 (or even self.brightness for max intensity)
flicker_brightness_offset = random.randint(-int(self.brightness // 1.5), int(self.brightness // 1.5))
flicker_brightness = max(0, min(255, self.brightness + flicker_brightness_offset))
flicker_color = self.apply_brightness(base_color, brightness_override=flicker_brightness)
self.fill(flicker_color)
self.last_update = current_time
return max(1, int(self.delay // 5))
def scanner(self):
"""
Mimics a 'Knight Rider' style scanner, moving in one direction.
"""
current_time = utime.ticks_ms()
self.fill((0, 0, 0)) # Clear all LEDs
# Calculate the head and tail position
head_pos = self.pattern_step
color = self.apply_brightness(self.colors[0])
# Draw the head
if 0 <= head_pos < self.num_leds:
self.n[head_pos] = color
# Draw the trailing pixels with decreasing brightness
for i in range(1, self.scanner_tail_length + 1):
tail_pos = head_pos - i
if 0 <= tail_pos < self.num_leds:
# Calculate fading color for tail
# Example: linear fade from full brightness to off
fade_factor = 1.0 - (i / (self.scanner_tail_length + 1))
faded_color = tuple(int(c * fade_factor) for c in color)
self.n[tail_pos] = faded_color
self.n.write()
self.pattern_step += 1
if self.pattern_step >= self.num_leds + self.scanner_tail_length:
self.pattern_step = 0 # Reset to start
self.last_update = current_time
return self.delay
def bidirectional_scanner(self):
"""
Mimics a 'Knight Rider' style scanner, moving back and forth.
"""
current_time = utime.ticks_ms()
self.fill((0, 0, 0)) # Clear all LEDs
color = self.apply_brightness(self.colors[0])
# Calculate the head position based on direction
head_pos = self.pattern_step
# Draw the head
if 0 <= head_pos < self.num_leds:
self.n[head_pos] = color
# Draw the trailing pixels with decreasing brightness
for i in range(1, self.scanner_tail_length + 1):
tail_pos = head_pos - (i * self.scanner_direction)
if 0 <= tail_pos < self.num_leds:
fade_factor = 1.0 - (i / (self.scanner_tail_length + 1))
faded_color = tuple(int(c * fade_factor) for c in color)
self.n[tail_pos] = faded_color
self.n.write()
self.pattern_step += self.scanner_direction
# Change direction if boundaries are reached
if self.scanner_direction == 1 and self.pattern_step >= self.num_leds:
self.scanner_direction = -1
self.pattern_step = self.num_leds - 1 # Start moving back from the last LED
elif self.scanner_direction == -1 and self.pattern_step < 0:
self.scanner_direction = 1
self.pattern_step = 0 # Start moving forward from the first LED
self.last_update = current_time
return self.delay
def fill_range(self):
"""
Fills a range of LEDs from n1 to n2 with a solid color.
If self.oneshot is True, it fills once and then turns off the LEDs.
"""
current_time = utime.ticks_ms()
if self.oneshot and self.pattern_step >= 1:
self.fill((0, 0, 0)) # Turn off LEDs if one-shot already happened
else:
color = self.apply_brightness(self.colors[0])
for i in range(self.n1, self.n2 + 1):
self.n[i] = color
self.n.write()
self.last_update = current_time
return self.delay
self.last_update = current_time
return self.delay
def n_chase(self):
"""
A theater chase pattern using n1 for on-width and n2 for off-width.
"""
current_time = utime.ticks_ms()
segment_length = self.n1 + self.n2
if segment_length == 0: # Avoid division by zero
self.fill((0,0,0))
self.n.write()
self.last_update = current_time
return self.delay
for i in range(self.num_leds):
if (i + self.pattern_step) % segment_length < self.n1:
self.n[i] = self.apply_brightness(self.colors[0])
else:
self.n[i] = (0, 0, 0)
self.n.write()
self.pattern_step = (self.pattern_step + 1) % segment_length
self.last_update = current_time
return self.delay
def alternating(self):
"""
An alternating pattern where n1 LEDs are ON/OFF and n2 LEDs are OFF/ON globally, without moving.
"""
current_time = utime.ticks_ms()
total_segment_length = self.n1 + self.n2
if total_segment_length == 0:
self.fill((0,0,0))
self.n.write()
self.last_update = current_time
return self.delay
# current_phase will alternate between 0 and 1
current_phase = self.pattern_step % 2
for i in range(self.num_leds):
# Position within a single repeating segment (n1 + n2)
pos_in_segment = i % total_segment_length
if current_phase == 0: # State 0: n1 ON, n2 OFF
if pos_in_segment < self.n1:
self.n[i] = self.apply_brightness(self.colors[0]) # n1 is ON
else:
self.n[i] = (0, 0, 0) # n2 is OFF
else: # State 1: n1 OFF, n2 ON
if pos_in_segment < self.n1:
self.n[i] = (0, 0, 0) # n1 is OFF
else:
self.n[i] = self.apply_brightness(self.colors[0]) # n2 is ON
self.n.write()
self.pattern_step = (self.pattern_step + 1) % 2 # Toggle between 0 and 1
self.last_update = current_time
return self.delay * 2
def pulse(self):
if self.pattern_step == 0:
self.fill(self.apply_brightness(self.colors[0]))
self.pattern_step = 1
self.last_update = utime.ticks_ms()
if utime.ticks_diff(utime.ticks_ms(), self.last_update) > self.delay:
self.fill((0, 0, 0))
print(utime.ticks_diff(utime.ticks_ms(), self.last_update))
self.run = False
return self.delay
if __name__ == "__main__":
import time
from machine import WDT
wdt = WDT(timeout=2000) # Enable watchdog with a 2 second timeout
p = Patterns(pin=4, num_leds=60, color1=(255,0,0), color2=(0,0,255), brightness=127, selected="off", delay=100)
print(p.colors, p.brightness)
tests = [
("off", {"duration_ms": 500}),
("on", {"duration_ms": 500}),
("color_wipe", {"delay": 200, "duration_ms": 1000}),
("rainbow_cycle", {"delay": 100, "duration_ms": 2500}),
("theater_chase", {"on_width": 3, "off_width": 3, "delay": 1000, "duration_ms": 2500}),
("blink", {"delay": 500, "duration_ms": 2000}),
("color_transition", {"delay": 150, "colors": [(255,0,0),(0,255,0),(0,0,255)], "duration_ms": 5000}),
("flicker", {"delay": 100, "duration_ms": 2000}),
("scanner", {"delay": 150, "duration_ms": 2500}),
("bidirectional_scanner", {"delay": 50, "duration_ms": 2500}),
("fill_range", {"n1": 10, "n2": 20, "delay": 500, "duration_ms": 2000}),
("n_chase", {"n1": 5, "n2": 5, "delay": 2000, "duration_ms": 2500}),
("alternating", {"n1": 5, "n2": 5, "delay": 500, "duration_ms": 2500}),
("pulse", {"delay": 100, "duration_ms": 700}),
]
print("\n--- Running pattern self-test ---")
for name, cfg in tests:
print(f"\nPattern: {name}")
# apply simple config helpers
if "delay" in cfg:
p.set_delay(cfg["delay"])
if "on_width" in cfg:
p.set_on_width(cfg["on_width"])
if "off_width" in cfg:
p.set_off_width(cfg["off_width"])
if "n1" in cfg and "n2" in cfg:
p.set_fill_range(cfg["n1"], cfg["n2"])
if "colors" in cfg:
p.set_colors(cfg["colors"])
p.select(name)
# run per configured duration using absolute-scheduled tick(next_due_ms)
start = utime.ticks_ms()
duration_ms = cfg["duration_ms"]
delay = cfg.get("delay", 0)
next_due = utime.ticks_ms() - 1 # force immediate first call
while utime.ticks_diff(utime.ticks_ms(), start) < duration_ms:
delay = p.tick(delay)
wdt.feed()
print("\n--- Test routine finished ---")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -1,9 +0,0 @@
import settings
import wifi
from settings import Settings
s = Settings()
name = s.get('name', 'led')
password = s.get("ap_password", "")
# wifi.ap(name, password)

View File

@@ -1,23 +1,24 @@
import patterns
from settings import Settings
from web import web
from patterns import Patterns
import gc
import utime
import machine
import time
import wifi
import json
from p2p import p2p
import espnow
import network
import asyncio
import json
import machine
def main():
settings = Settings()
print(settings)
patterns = Patterns(settings["led_pin"], settings["num_leds"], selected="off")
if settings.get("color_order", "rgb") == "rbg":
color_order = (1, 5, 3)
else:
color_order = (1, 3, 5)
patterns = Patterns(settings["led_pin"], settings["num_leds"], brightness=255)
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
@@ -29,14 +30,22 @@ def main():
wdt = machine.WDT(timeout=10000)
wdt.feed()
#print mac in hex
print("Mac address", sta_if.config("mac").hex())
print("Patterns", patterns.colors)
print("Patterns", patterns.selected)
patterns.select(patterns.selected)
while True:
# advance pattern based on its own returned schedule
# due = patterns.tick(due)
wdt.feed()
patterns.tick()
# Drain all pending packets and only process the latest
last_msg = None
while True:
host, msg = e.recv(0)
if not msg:
break
@@ -46,40 +55,25 @@ def main():
try:
data = json.loads(last_msg)
print(data)
defaults = data.get("d", {})
bar = data.get(settings.get("name"), {})
# Check message type
message_type = defaults.get("t", "b") # Default to beat if not specified
# Always update parameters from message
param_mapping = {
"br": "brightness",
"dl": "delay",
"cl": "colors",
"n1": "n1",
"n2": "n2",
"n3": "n3",
"n4": "n4",
"s": "step"
}
# Iterate through values in bar and update parameters
print("Bar values:")
for key, value in bar.items():
print(f" {key}: {value}")
# Update parameter if it exists in param_mapping
if key in param_mapping:
attr_name = param_mapping[key]
current_value = getattr(patterns, attr_name)
new_value = bar.get(key, defaults.get(key, current_value))
setattr(patterns, attr_name, new_value)
# Print received parameters
patterns.brightness = data.get("brightness", patterns.brightness)
patterns.delay = data.get("delay", patterns.delay)
patterns.colors = data.get("colors", patterns.colors)
patterns.selected = data.get("pattern", patterns.selected)
patterns.n1 = data.get("n1", patterns.n1)
patterns.n2 = data.get("n2", patterns.n2)
patterns.n3 = data.get("n3", patterns.n3)
patterns.n4 = data.get("n4", patterns.n4)
patterns.step = data.get("step", patterns.step)
patterns.auto = data.get("auto", patterns.auto)
patterns.select(patterns.selected)
print("Selected pattern", patterns.selected)
except Exception as ex:
print(f"Failed to load espnow data {last_msg}: {ex}")
continue
finally:
gc.collect()
main()

File diff suppressed because it is too large Load Diff

View File

@@ -1,70 +0,0 @@
from machine import Pin
from neopixel import NeoPixel
import utime
class PatternBase:
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 = {}
self.selected = selected
self.run = True
# Ensure colors list always starts with at least two for robust transition handling
self.colors = [color1, color2] if color1 != color2 else [color1, (255, 255, 255)] # Fallback if initial colors are same
if not self.colors: # Ensure at least one color exists
self.colors = [(0, 0, 0)]
self.transition_duration = delay * 50 # Default transition duration
self.hold_duration = delay * 10 # Default hold duration at each color
self.transition_step = 0 # Current step in the transition
self.current_color_idx = 0 # Index of the color currently being held/transitioned from
self.current_color = self.colors[self.current_color_idx] # The actual blended color
self.hold_start_time = utime.ticks_ms() # Time when the current color hold started
# New attributes for scanner patterns (moved from Patterns to PatternBase as they are generic enough)
self.scanner_direction = 1 # 1 for forward, -1 for backward
self.scanner_tail_length = 3 # Number of trailing pixels
# Store last pattern-returned delay to use for subsequent gating
self._last_returned_delay = None
def update_num_leds(self, pin, num_leds):
self.n = NeoPixel(Pin(pin, Pin.OUT), num_leds)
self.num_leds = num_leds
def set_color(self, num, color):
if 0 <= num < len(self.colors):
self.colors[num] = color
elif num == len(self.colors): # Allow setting a new color at the end
self.colors.append(color)
return True
return False
def del_color(self, num):
if 0 <= num < len(self.colors):
del self.colors[num]
return True
return False
def apply_brightness(self, color, brightness_override=None):
effective_brightness = brightness_override if brightness_override is not None else self.brightness
return tuple(int(c * effective_brightness / 255) for c in color)
def write(self):
self.n.write()
def fill(self, color=None):
fill_color = color if color is not None else self.colors[0]
self.n.fill(fill_color)
self.n.write()
def off(self):
self.fill((0, 0, 0))
def on(self):
self.fill(self.apply_brightness(self.colors[0]))

View File

@@ -9,14 +9,21 @@ class Settings(dict):
def __init__(self):
super().__init__()
self.load() # Load settings from file during initialization
if self.get("color_order", "rgb") == "rbg": self.color_order = (1, 5, 3)
if self["color_order"] == "rbg": self.color_order = (1, 5, 3)
else: self.color_order = (1, 3, 5)
def set_defaults(self):
self["led_pin"] = 10
self["num_leds"] = 119
self["num_leds"] = 50
self["pattern"] = "on"
self["color1"] = "#00ff00"
self["color2"] = "#ff0000"
self["delay"] = 100
self["brightness"] = 10
self["color_order"] = "rgb"
self["name"] = f"104"
self["name"] = f"led-{ubinascii.hexlify(wifi.get_mac()).decode()}"
self["ap_password"] = ""
self["id"] = 0
def save(self):
try:
@@ -40,6 +47,7 @@ class Settings(dict):
def set_settings(self, data, patterns, save):
try:
print(data)
for key, value in data.items():
print(key, value)
if key == "colors":
@@ -62,24 +70,6 @@ class Settings(dict):
elif key == "brightness":
brightness = int(data["brightness"])
patterns.set_brightness(brightness)
elif key == "on_width":
on_width = int(data["on_width"])
patterns.set_on_width(on_width)
elif key == "off_width":
off_width = int(data["off_width"])
on_width = int(data.get("on_width", self["on_width"]))
patterns.set_on_off_width(on_width, off_width)
elif key == "n1":
n1 = int(data["n1"])
n2 = int(data.get("n2", patterns.n2))
patterns.set_fill_range(n1, n2)
elif key == "n2":
n2 = int(data["n2"])
n1 = int(data.get("n1", patterns.n1))
patterns.set_fill_range(n1, n2)
elif key == "oneshot":
oneshot_value = bool(data["oneshot"])
patterns.set_oneshot(oneshot_value)
elif key == "name":
self[key] = value
self.save()
@@ -100,8 +90,7 @@ class Settings(dict):
if save:
self.save()
return "OK", 200
except Exception as e:
print(f"An unexpected error occurred in set_settings: {e}")
except (KeyError, ValueError):
return "Bad request", 400
# Example usage

View File

@@ -1,109 +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 0.2s;
}
input[type="range"]:hover {
opacity: 1;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 25px;
height: 25px;
background: #4caf50;
cursor: pointer;
border-radius: 50%;
}
input[type="range"]::-moz-range-thumb {
width: 25px;
height: 25px;
background: #4caf50;
cursor: pointer;
border-radius: 50%;
}
#pattern_buttons {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 20px;
}
#pattern_buttons button {
flex: 1 0 calc(33.333% - 10px);
padding: 10px;
background-color: #4caf50;
color: white;
border: none;
cursor: pointer;
transition: background-color 0.3s;
}
#pattern_buttons button:hover {
background-color: #45a049;
}
@media (max-width: 480px) {
#pattern_buttons button {
flex: 1 0 calc(50% - 10px);
}
}
#connection-status {
width: 15px;
height: 15px;
border-radius: 50%;
display: inline-block; /* Or block, depending on where you put it */
margin-left: 10px; /* Adjust spacing as needed */
vertical-align: middle; /* Align with nearby text */
background-color: grey; /* Default: Unknown */
}
#connection-status.connecting {
background-color: yellow;
}
#connection-status.open {
background-color: green;
}
#connection-status.closing,
#connection-status.closed {
background-color: red;
}
#color_order_form label,
#color_order_form input[type="radio"] {
/* Ensures they behave as inline elements */
display: inline-block;
/* Adds some space between them for readability */
margin-right: 10px;
vertical-align: middle; /* Aligns them nicely if heights vary */
}

View File

@@ -1,244 +0,0 @@
let delayTimeout;
let brightnessTimeout;
let colorTimeout;
let color2Timeout;
let ws; // Variable to hold the WebSocket connection
let connectionStatusElement; // Variable to hold the connection status element
// Function to update the connection status indicator
function updateConnectionStatus(status) {
if (!connectionStatusElement) {
connectionStatusElement = document.getElementById("connection-status");
}
if (connectionStatusElement) {
connectionStatusElement.className = ""; // Clear existing classes
connectionStatusElement.classList.add(status);
// Optionally, you could also update text content based on status
// connectionStatusElement.textContent = status.charAt(0).toUpperCase() + status.slice(1);
}
}
// Function to establish WebSocket connection
function connectWebSocket() {
// Determine the WebSocket URL based on the current location
const wsUrl = `ws://${window.location.host}/ws`;
ws = new WebSocket(wsUrl);
updateConnectionStatus("connecting"); // Indicate connecting state
ws.onopen = function (event) {
console.log("WebSocket connection opened:", event);
updateConnectionStatus("open"); // Indicate open state
// Optionally, you could send an initial message here
};
ws.onmessage = function (event) {
console.log("WebSocket message received:", event.data);
};
ws.onerror = function (event) {
console.error("WebSocket error:", event);
updateConnectionStatus("closed"); // Indicate error state (treat as closed)
};
ws.onclose = function (event) {
if (event.wasClean) {
console.log(
`WebSocket connection closed cleanly, code=${event.code}, reason=${event.reason}`,
);
updateConnectionStatus("closed"); // Indicate closed state
} else {
console.error("WebSocket connection died");
updateConnectionStatus("closed"); // Indicate closed state
}
// Attempt to reconnect after a delay
setTimeout(connectWebSocket, 1000);
};
}
// Function to send data over WebSocket
function sendWebSocketData(data) {
if (ws && ws.readyState === WebSocket.OPEN) {
console.log("Sending data over WebSocket:", data);
ws.send(JSON.stringify(data));
} else {
console.error("WebSocket is not connected. Cannot send data:", data);
// You might want to queue messages or handle this in a different way
}
}
// Keep the post and get functions for now, they might still be useful
async function post(path, data) {
console.log(`POST to ${path}`, data);
try {
const response = await fetch(path, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
} catch (error) {
console.error("Error during POST request:", error);
}
}
async function get(path) {
try {
const response = await fetch(path);
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error("Error during GET request:", error);
}
}
async function updateColor(event) {
event.preventDefault();
clearTimeout(colorTimeout);
colorTimeout = setTimeout(function () {
const color = document.getElementById("color").value;
sendWebSocketData({ color1: color });
}, 500);
}
async function updateColor2(event) {
event.preventDefault();
clearTimeout(color2Timeout);
color2Timeout = setTimeout(function () {
const color = document.getElementById("color2").value;
sendWebSocketData({ color2: color });
}, 500);
}
async function updatePattern(pattern) {
sendWebSocketData({ pattern: pattern });
}
async function updateBrightness(event) {
event.preventDefault();
clearTimeout(brightnessTimeout);
brightnessTimeout = setTimeout(function () {
const brightness = document.getElementById("brightness").value;
sendWebSocketData({ brightness: brightness });
}, 500);
}
async function updateDelay(event) {
event.preventDefault();
clearTimeout(delayTimeout);
delayTimeout = setTimeout(function () {
const delay = document.getElementById("delay").value;
sendWebSocketData({ delay: delay });
}, 500);
}
async function updateNumLeds(event) {
event.preventDefault();
const numLeds = document.getElementById("num_leds").value;
sendWebSocketData({ num_leds: parseInt(numLeds) });
}
async function updateName(event) {
event.preventDefault();
const name = document.getElementById("name").value;
sendWebSocketData({ name: name });
}
async function updateID(event) {
event.preventDefault();
const id = document.getElementById("id").value;
sendWebSocketData({ id: parseInt(id) });
}
async function updateLedPin(event) {
event.preventDefault();
const ledpin = document.getElementById("led_pin").value;
sendWebSocketData({ led_pin: parseInt(ledpin) });
}
function handleRadioChange(event) {
event.preventDefault();
console.log("Selected color order:", event.target.value);
// Add your specific logic here
if (event.target.value === "rgb") {
console.log("RGB order selected!");
} else if (event.target.value === "rbg") {
console.log("RBG order selected!");
}
sendWebSocketData({ color_order: event.target.value });
}
function createPatternButtons(patterns) {
const container = document.getElementById("pattern_buttons");
container.innerHTML = ""; // Clear previous buttons
patterns.forEach((pattern) => {
const button = document.createElement("button");
button.type = "button";
button.textContent = pattern;
button.value = pattern;
button.addEventListener("click", async function (event) {
event.preventDefault();
await updatePattern(pattern);
});
container.appendChild(button);
});
}
document.addEventListener("DOMContentLoaded", async function () {
// Get the connection status element once the DOM is ready
connectionStatusElement = document.getElementById("connection-status");
// Establish WebSocket connection on page load
connectWebSocket();
document.getElementById("color").addEventListener("input", updateColor);
document.getElementById("color2").addEventListener("input", updateColor2);
document.getElementById("delay").addEventListener("input", updateDelay);
document
.getElementById("brightness")
.addEventListener("input", updateBrightness);
document
.getElementById("num_leds_form")
.addEventListener("submit", updateNumLeds);
document.getElementById("name_form").addEventListener("submit", updateName);
document.getElementById("id_form").addEventListener("submit", updateID);
document
.getElementById("led_pin_form")
.addEventListener("submit", updateLedPin);
document.getElementById("delay").addEventListener("touchend", updateDelay);
document
.getElementById("brightness")
.addEventListener("touchend", updateBrightness);
document.getElementById("rgb").addEventListener("change", handleRadioChange);
document.getElementById("rbg").addEventListener("change", handleRadioChange);
document.querySelectorAll(".pattern_button").forEach((button) => {
console.log(button.value);
button.addEventListener("click", async (event) => {
event.preventDefault();
await updatePattern(button.value);
});
});
});
// Function to toggle the display of the settings menu
function selectSettings() {
const settingsMenu = document.getElementById("settings_menu");
controls = document.getElementById("controls");
settingsMenu.style.display = "block";
controls.style.display = "none";
}
function selectControls() {
const settingsMenu = document.getElementById("settings_menu");
controls = document.getElementById("controls");
settingsMenu.style.display = "none";
controls.style.display = "block";
}

View File

@@ -1,124 +0,0 @@
{% args settings, patterns, mac %}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{settings['name']}}</title>
<script src="static/main.js"></script>
<link rel="stylesheet" href="static/main.css" />
</head>
<body>
<h1>{{settings['name']}}</h1>
<button onclick="selectControls()">Controls</button>
<button onclick="selectSettings()">Settings</button>
<!-- Main LED Controls -->
<div id="controls">
<div id="pattern_buttons">
{% for p in patterns %}
<button class="pattern_button" value="{{p}}">{{p}}</button>
{% endfor %}
<!-- Pattern buttons will be inserted here -->
</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>
<form id="name_form" method="post" action="/name">
<label for="name">Name:</label>
<input
type="text"
id="name"
name="num_leds"
value="{{settings['name']}}"
/>
<input type="submit" value="Update Name" />
</form>
<form id="id_form" method="post" action="/id">
<label for="id">ID:</label>
<input
type="text"
id="id"
name="id"
value="{{settings['id']}}"
/>
<input type="submit" value="Update ID" />
</form>
<!-- Separate form for submitting num_leds -->
<form id="num_leds_form" method="post" action="/num_leds">
<label for="num_leds">Number of LEDs:</label>
<input
type="text"
id="num_leds"
name="num_leds"
value="{{settings['num_leds']}}"
/>
<input type="submit" value="Update Number of LEDs" />
</form>
<form id="led_pin_form" method="post" action="/led_pin">
<label for="num_leds">Led pin:</label>
<input
type="text"
id="led_pin"
name="led_pin"
value="{{settings['led_pin']}}"
/>
<input type="submit" value="Update Led Pin" />
</form>
<form id="color_order_form">
<label for="rgb">RGB:</label>
<input type="radio" id="rgb" name="color_order" value="rgb" {{'checked' if settings["color_order"]=="rgb" else ''}} />
<label for="rbg">RBG</label>
<input type="radio" id="rbg" name="color_order" value="rbg" {{'checked' if settings["color_order"]=="rbg" else ''}}/>
</form>
<p>Mac address: {{mac}}</p>
</div>
<div id="connection-status"></div>
</body>
</html>

View File

@@ -1,43 +0,0 @@
from microdot import Microdot, send_file, Response
from microdot.utemplate import Template
from microdot.websocket import with_websocket
import machine
import wifi
import json
def web(settings, patterns):
app = Microdot()
Response.default_content_type = 'text/html'
@app.route('/')
async def index_hnadler(request):
mac = wifi.get_mac().hex()
return Template('/index.html').render(settings=settings, patterns=patterns.patterns.keys(), mac=mac)
@app.route("/static/<path:path>")
def static_handler(request, path):
if '..' in path:
# Directory traversal is not allowed
return 'Not found', 404
return send_file('static/' + path)
@app.post("/settings")
def settings_handler(request):
# Keep the POST handler for compatibility or alternative usage if needed
# For WebSocket updates, the /ws handler is now primary
return settings.set_settings(request.body.decode('utf-8'), patterns)
@app.route("/ws")
@with_websocket
async def ws(request, ws):
while True:
data = await ws.receive()
if data:
# Process the received data
_, status_code = settings.set_settings(json.loads(data), patterns, True)
#await ws.send(status_code)
else:
break
return app

View File

@@ -1,39 +0,0 @@
import network
from time import sleep
def connect(ssid, password, ip, gateway):
try:
sta_if = network.WLAN(network.STA_IF)
if not sta_if.isconnected():
if ssid == "" or password == "":
print("Missing ssid or password")
return None
if ip != "" and gateway != "":
sta_if.ifconfig((ip, '255.255.255.0', gateway, '1.1.1.1'))
print('connecting to network...')
sta_if.active(True)
sta_if.connect(ssid, password)
sleep(0.1)
if sta_if.isconnected():
return sta_if.ifconfig()
return None
return sta_if.ifconfig()
except Exception as e:
print(f"Failed to connect to wifi {e}")
return None
def ap(ssid, password):
ap_if = network.WLAN(network.AP_IF)
ap_mac = ap_if.config('mac')
print(ssid)
ap_if.active(True)
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')

View File

@@ -1,58 +0,0 @@
#!/usr/bin/env python3
"""
Test script for blink pattern
Run with: mpremote run test/blink.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
def test_blink():
print("Testing blink pattern...")
# Initialize watchdog timer
wdt = WDT(timeout=10000) # 10 second timeout
wdt.feed()
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"], # Set to 200 LEDs for test
brightness=255, # Full brightness
delay=500
)
# Set a bright red color
p.colors = [(255, 0, 0)] # Red
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {p.num_leds} (test override: 200)")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Color: {p.colors[0]}")
# Test blink pattern
print("Starting blink pattern for 5 seconds...")
p.select("bl")
# Let it run for 5 seconds with WDT feeding
start_time = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start_time) < 5000:
wdt.feed()
utime.sleep_ms(100)
# Stop the pattern
p.run = False
print("Blink test completed")
# Turn off LEDs
p.off()
print("LEDs turned off")
if __name__ == "__main__":
test_blink()

View File

@@ -1,58 +0,0 @@
#!/usr/bin/env python3
"""
Circle loading test 1: n1=50, n2=100, n3=200, n4=0 (Red)
Runs forever
Run with: mpremote run test/circle/1.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Circle Loading Test 1: n1=50, n2=100, n3=200, n4=0 (Red)")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=2000
)
# Configure test parameters
p.n1 = 50 # Head moves 50 LEDs/second
p.n2 = 100 # Max length 100 LEDs
p.n3 = 200 # Tail moves 200 LEDs/second
p.n4 = 0 # Min length 0 LEDs
p.colors = [(255, 0, 0)] # Red
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Parameters: n1={p.n1}, n2={p.n2}, n3={p.n3}, n4={p.n4}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("cl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,58 +0,0 @@
#!/usr/bin/env python3
"""
Circle loading test 2: n1=10, n2=30, n3=20, n4=5 (Green)
Runs forever
Run with: mpremote run test/circle/2.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Circle Loading Test 2: n1=10, n2=30, n3=20, n4=5 (Green)")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=2000
)
# Configure test parameters
p.n1 = 10 # Head moves 10 LEDs/second
p.n2 = 30 # Max length 30 LEDs
p.n3 = 20 # Tail moves 20 LEDs/second
p.n4 = 5 # Min length 5 LEDs
p.colors = [(0, 255, 0)] # Green
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Parameters: n1={p.n1}, n2={p.n2}, n3={p.n3}, n4={p.n4}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("cl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,58 +0,0 @@
#!/usr/bin/env python3
"""
Circle loading test 3: n1=15, n2=25, n3=15, n4=3 (Blue)
Runs forever
Run with: mpremote run test/circle/3.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Circle Loading Test 3: n1=15, n2=25, n3=15, n4=3 (Blue)")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=2000
)
# Configure test parameters
p.n1 = 15 # Head moves 15 LEDs/second
p.n2 = 25 # Max length 25 LEDs
p.n3 = 15 # Tail moves 15 LEDs/second
p.n4 = 3 # Min length 3 LEDs
p.colors = [(0, 0, 255)] # Blue
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Parameters: n1={p.n1}, n2={p.n2}, n3={p.n3}, n4={p.n4}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("cl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,58 +0,0 @@
#!/usr/bin/env python3
"""
Circle loading test 4: n1=30, n2=40, n3=5, n4=8 (Yellow)
Runs forever
Run with: mpremote run test/circle/4.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Circle Loading Test 4: n1=30, n2=40, n3=5, n4=8 (Yellow)")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=2000
)
# Configure test parameters
p.n1 = 30 # Head moves 30 LEDs/second
p.n2 = 40 # Max length 40 LEDs
p.n3 = 5 # Tail moves 5 LEDs/second
p.n4 = 8 # Min length 8 LEDs
p.colors = [(255, 255, 0)] # Yellow
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Parameters: n1={p.n1}, n2={p.n2}, n3={p.n3}, n4={p.n4}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("cl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,58 +0,0 @@
#!/usr/bin/env python3
"""
Circle loading test 5: n1=25, n2=60, n3=30, n4=2 (Magenta)
Runs forever
Run with: mpremote run test/circle/5.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Circle Loading Test 5: n1=25, n2=60, n3=30, n4=2 (Magenta)")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=2000
)
# Configure test parameters
p.n1 = 25 # Head moves 25 LEDs/second
p.n2 = 60 # Max length 60 LEDs
p.n3 = 30 # Tail moves 30 LEDs/second
p.n4 = 2 # Min length 2 LEDs
p.colors = [(255, 0, 255)] # Magenta
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Parameters: n1={p.n1}, n2={p.n2}, n3={p.n3}, n4={p.n4}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("cl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,58 +0,0 @@
#!/usr/bin/env python3
"""
Circle loading test 6: n1=5, n2=20, n3=40, n4=1 (Cyan)
Runs forever
Run with: mpremote run test/circle/6.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Circle Loading Test 6: n1=5, n2=20, n3=40, n4=1 (Cyan)")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=2000
)
# Configure test parameters
p.n1 = 5 # Head moves 5 LEDs/second
p.n2 = 20 # Max length 20 LEDs
p.n3 = 40 # Tail moves 40 LEDs/second
p.n4 = 1 # Min length 1 LED
p.colors = [(0, 255, 255)] # Cyan
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Parameters: n1={p.n1}, n2={p.n2}, n3={p.n3}, n4={p.n4}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("cl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,58 +0,0 @@
#!/usr/bin/env python3
"""
Circle loading test 7: n1=12, n2=15, n3=18, n4=4 (Orange)
Runs forever
Run with: mpremote run test/circle/7.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Circle Loading Test 7: n1=12, n2=15, n3=18, n4=4 (Orange)")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=2000
)
# Configure test parameters
p.n1 = 12 # Head moves 12 LEDs/second
p.n2 = 15 # Max length 15 LEDs
p.n3 = 18 # Tail moves 18 LEDs/second
p.n4 = 4 # Min length 4 LEDs
p.colors = [(255, 128, 0)] # Orange
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Parameters: n1={p.n1}, n2={p.n2}, n3={p.n3}, n4={p.n4}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("cl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,58 +0,0 @@
#!/usr/bin/env python3
"""
Circle loading test 8: n1=100, n2=50, n3=150, n4=10 (Purple)
Runs forever
Run with: mpremote run test/circle/8.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Circle Loading Test 8: n1=100, n2=50, n3=150, n4=10 (Purple)")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=2000
)
# Configure test parameters
p.n1 = 100 # Head moves 100 LEDs/second
p.n2 = 50 # Max length 50 LEDs
p.n3 = 150 # Tail moves 150 LEDs/second
p.n4 = 10 # Min length 10 LEDs
p.colors = [(128, 0, 255)] # Purple
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Parameters: n1={p.n1}, n2={p.n2}, n3={p.n3}, n4={p.n4}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("cl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,128 +0,0 @@
#!/usr/bin/env python3
"""
Test script for circle loading pattern with multiple test cases
Run with: mpremote run test/circle_loading.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
def run_test_case(p, test_name, duration_ms=5000):
"""Run a test case for specified duration"""
print(f"\n--- {test_name} ---")
print(f"Parameters: n1={p.n1} head/sec, n2={p.n2} max length, n3={p.n3} tail/sec, n4={p.n4} min length")
print(f"Color: {p.colors[0]}")
print(f"Running for {duration_ms//1000} seconds...")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("cl")
# Run for specified duration
start_time = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start_time) < duration_ms:
wdt.feed()
utime.sleep_ms(100)
# Stop pattern
p.run = False
print(f"{test_name} completed")
# Brief pause between tests
utime.sleep_ms(500)
def test_circle_loading():
print("Testing circle loading pattern with multiple configurations...")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=2000 # Base cycle time
)
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
# Test Case 1: Your specified parameters
p.n1 = 50
p.n2 = 100
p.n3 = 200
p.n4 = 0
p.colors = [(255, 0, 0)] # Red
run_test_case(p, "Test 1: n1=50, n2=100, n3=200, n4=0", 15000)
# Test Case 2: Slow head, fast tail
p.n1 = 10 # Head moves 10 LEDs/second
p.n2 = 30 # Max length 30 LEDs
p.n3 = 20 # Tail moves 20 LEDs/second
p.n4 = 5 # Min length 5 LEDs
p.colors = [(0, 255, 0)] # Green
run_test_case(p, "Test 2: n1=10, n2=30, n3=20, n4=5", 10000)
# Test Case 3: Equal head and tail speed
p.n1 = 15 # Head moves 15 LEDs/second
p.n2 = 25 # Max length 25 LEDs
p.n3 = 15 # Tail moves 15 LEDs/second
p.n4 = 3 # Min length 3 LEDs
p.colors = [(0, 0, 255)] # Blue
run_test_case(p, "Test 3: n1=15, n2=25, n3=15, n4=3", 8000)
# Test Case 4: Very fast head, slow tail
p.n1 = 30 # Head moves 30 LEDs/second
p.n2 = 40 # Max length 40 LEDs
p.n3 = 5 # Tail moves 5 LEDs/second
p.n4 = 8 # Min length 8 LEDs
p.colors = [(255, 255, 0)] # Yellow
run_test_case(p, "Test 4: n1=30, n2=40, n3=5, n4=8", 12000)
# Test Case 5: Long segment, fast cycle
p.n1 = 25 # Head moves 25 LEDs/second
p.n2 = 60 # Max length 60 LEDs
p.n3 = 30 # Tail moves 30 LEDs/second
p.n4 = 2 # Min length 2 LEDs
p.colors = [(255, 0, 255)] # Magenta
run_test_case(p, "Test 5: n1=25, n2=60, n3=30, n4=2", 10000)
# Test Case 6: Very slow head, very fast tail
p.n1 = 5 # Head moves 5 LEDs/second
p.n2 = 20 # Max length 20 LEDs
p.n3 = 40 # Tail moves 40 LEDs/second
p.n4 = 1 # Min length 1 LED
p.colors = [(0, 255, 255)] # Cyan
run_test_case(p, "Test 6: n1=5, n2=20, n3=40, n4=1", 8000)
# Test Case 7: Medium speeds, short segment
p.n1 = 12 # Head moves 12 LEDs/second
p.n2 = 15 # Max length 15 LEDs
p.n3 = 18 # Tail moves 18 LEDs/second
p.n4 = 4 # Min length 4 LEDs
p.colors = [(255, 128, 0)] # Orange
run_test_case(p, "Test 7: n1=12, n2=15, n3=18, n4=4", 6000)
# Test Case 8: Ultra fast everything
p.n1 = 100 # Head moves 100 LEDs/second
p.n2 = 50 # Max length 50 LEDs
p.n3 = 150 # Tail moves 150 LEDs/second
p.n4 = 10 # Min length 10 LEDs
p.colors = [(128, 0, 255)] # Purple
run_test_case(p, "Test 8: n1=100, n2=50, n3=150, n4=10", 5000)
print("\n=== All tests completed ===")
# Turn off LEDs
p.off()
print("LEDs turned off")
if __name__ == "__main__":
test_circle_loading()

View File

@@ -1,56 +0,0 @@
#!/usr/bin/env python3
"""
Flicker test 1: Red, delay=50ms, min brightness=50
Runs forever
Run with: mpremote run test/flicker/1.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Flicker Test 1: Red, delay=50ms, min brightness=50")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=50
)
# Configure test parameters
p.n1 = 50 # Min brightness 50
p.colors = [(255, 0, 0)] # Red
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Min brightness: {p.n1}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("fl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,56 +0,0 @@
#!/usr/bin/env python3
"""
Flicker test 2: Green, delay=100ms, min brightness=100
Runs forever
Run with: mpremote run test/flicker/2.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Flicker Test 2: Green, delay=100ms, min brightness=100")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=100
)
# Configure test parameters
p.n1 = 100 # Min brightness 100
p.colors = [(0, 255, 0)] # Green
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Min brightness: {p.n1}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("fl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,56 +0,0 @@
#!/usr/bin/env python3
"""
Flicker test 3: Blue, delay=200ms, min brightness=20
Runs forever
Run with: mpremote run test/flicker/3.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Flicker Test 3: Blue, delay=200ms, min brightness=20")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=200
)
# Configure test parameters
p.n1 = 20 # Min brightness 20
p.colors = [(0, 0, 255)] # Blue
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Min brightness: {p.n1}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("fl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,56 +0,0 @@
#!/usr/bin/env python3
"""
Flicker test 4: White, delay=80ms, min brightness=150
Runs forever
Run with: mpremote run test/flicker/4.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Flicker Test 4: White, delay=80ms, min brightness=150")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=80
)
# Configure test parameters
p.n1 = 150 # Min brightness 150
p.colors = [(255, 255, 255)] # White
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Min brightness: {p.n1}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("fl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,56 +0,0 @@
#!/usr/bin/env python3
"""
Flicker test 5: Yellow, delay=150ms, min brightness=30
Runs forever
Run with: mpremote run test/flicker/5.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Flicker Test 5: Yellow, delay=150ms, min brightness=30")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=150
)
# Configure test parameters
p.n1 = 30 # Min brightness 30
p.colors = [(255, 255, 0)] # Yellow
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Min brightness: {p.n1}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("fl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,56 +0,0 @@
#!/usr/bin/env python3
"""
Flicker test 6: Magenta, delay=60ms, min brightness=80
Runs forever
Run with: mpremote run test/flicker/6.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Flicker Test 6: Magenta, delay=60ms, min brightness=80")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=60
)
# Configure test parameters
p.n1 = 80 # Min brightness 80
p.colors = [(255, 0, 255)] # Magenta
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Min brightness: {p.n1}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("fl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,56 +0,0 @@
#!/usr/bin/env python3
"""
Flicker test 7: Cyan, delay=120ms, min brightness=5
Runs forever
Run with: mpremote run test/flicker/7.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Flicker Test 7: Cyan, delay=120ms, min brightness=5")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=120
)
# Configure test parameters
p.n1 = 5 # Min brightness 5
p.colors = [(0, 255, 255)] # Cyan
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Min brightness: {p.n1}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("fl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,56 +0,0 @@
#!/usr/bin/env python3
"""
Flicker test 8: Orange, delay=40ms, min brightness=180
Runs forever
Run with: mpremote run test/flicker/8.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Flicker Test 8: Orange, delay=40ms, min brightness=180")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=40
)
# Configure test parameters
p.n1 = 180 # Min brightness 180
p.colors = [(255, 128, 0)] # Orange
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Min brightness: {p.n1}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("fl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,158 +0,0 @@
import asyncio
import json
import argparse
import signal
try:
import websockets # type: ignore
except Exception as e:
print("Please install websockets: pip install websockets")
raise
WS_URI = "ws://192.168.4.1/ws"
# Default pattern suite aligned with current firmware patterns
PATTERN_SUITE = [
{"pattern": "flicker", "delay": 80, "iterations": 30, "repeat_delay": 80, "colors": ["#ffaa00"]},
{"pattern": "fill_range", "n1": 10, "n2": 20, "delay": 400, "iterations": 1, "repeat_delay": 500, "colors": ["#888888"]},
{"pattern": "n_chase", "n1": 5, "n2": 5, "delay": 250, "iterations": 40, "repeat_delay": 120, "colors": ["#00ff88"]},
{"pattern": "alternating", "n1": 6, "n2": 6, "delay": 300, "iterations": 20, "repeat_delay": 300, "colors": ["#ff8800"]},
{"pattern": "pulse", "delay": 200, "iterations": 6, "repeat_delay": 300, "colors": ["#ffffff"]},
]
def build_message(
pattern: str,
n: int | None = None,
delay: int | None = None,
colors: list[str] | None = None,
brightness: int | None = None,
num_leds: int | None = None,
n1: int | None = None,
n2: int | None = None,
name: str = "0",
pattern_step: int | None = None,
):
settings: dict[str, object] = {
"pattern": pattern,
}
if n is not None:
settings["n"] = n
if delay is not None:
settings["delay"] = delay
if colors is not None:
settings["colors"] = colors
if brightness is not None:
settings["brightness"] = brightness
if num_leds is not None:
settings["num_leds"] = num_leds
if n1 is not None:
settings["n1"] = n1
if n2 is not None:
settings["n2"] = n2
if pattern_step is not None:
settings["pattern_step"] = pattern_step
# ESP-NOW-style nested payload keyed by name (e.g., "0")
return {name: settings}
async def send_once(uri: str, payload: dict, hold_ms: int | None = None):
async with websockets.connect(uri) as ws:
await ws.send(json.dumps(payload))
if hold_ms and hold_ms > 0:
await asyncio.sleep(hold_ms / 1000)
async def run_suite(uri: str):
async with websockets.connect(uri) as ws:
for cfg in PATTERN_SUITE:
iterations = int(cfg.get("iterations", 10))
interval_ms = int(cfg.get("interval_ms", cfg.get("delay", 100) or 100))
repeat_ms = int(cfg.get("repeat_delay", interval_ms))
for i in range(iterations):
msg = build_message(
cfg.get("pattern", "off"),
i,
delay=cfg.get("delay"),
colors=cfg.get("colors"),
brightness=cfg.get("brightness", 255),
num_leds=cfg.get("num_leds"),
n1=cfg.get("n1"),
n2=cfg.get("n2"),
name=cfg.get("name", "0"),
pattern_step=cfg.get("pattern_step"),
)
print(msg)
await ws.send(json.dumps(msg))
await asyncio.sleep(repeat_ms / 1000)
def _parse_args():
p = argparse.ArgumentParser(description="WebSocket LED pattern tester")
p.add_argument("--uri", default=WS_URI, help="WebSocket URI, default ws://192.168.4.1/ws")
p.add_argument("--pattern", help="Single pattern to send (overrides suite)")
p.add_argument("--delay", type=int, help="Delay ms")
p.add_argument("--brightness", type=int, help="Brightness 0-255")
p.add_argument("--num-leds", type=int, help="Number of LEDs")
p.add_argument("--colors", nargs="*", help="Hex colors like #ff0000 #00ff00")
p.add_argument("--on-width", type=int)
p.add_argument("--off-width", type=int)
p.add_argument("--n1", type=int)
p.add_argument("--n2", type=int)
p.add_argument("--name", default="0", help="Target name key for nested payload (default: 0)")
p.add_argument("--iterations", type=int, help="How many cycles/messages to send")
p.add_argument("--interval", type=int, help="Interval between messages in ms (default: delay or 100)")
p.add_argument("--repeat-delay", dest="repeat_delay", type=int, help="Delay between repeats in ms (overrides --interval if set)")
p.add_argument("--hold", type=int, default=1500, help="Hold ms for single send")
return p.parse_args()
def _setup_sigint(loop: asyncio.AbstractEventLoop):
for sig in (signal.SIGINT, signal.SIGTERM):
try:
loop.add_signal_handler(sig, loop.stop)
except NotImplementedError:
pass
async def main_async():
args = _parse_args()
if args.pattern:
iterations = int(args.iterations or 1)
interval_ms = int(args.interval or (args.delay if args.delay is not None else 100))
repeat_ms = int(args.repeat_delay or interval_ms)
async with websockets.connect(args.uri) as ws:
for i in range(iterations):
msg = build_message(
pattern=args.pattern,
n=i,
delay=args.delay,
colors=args.colors,
brightness=args.brightness,
num_leds=args.num_leds,
n1=args.n1,
n2=args.n2,
name=args.name,
)
print(msg)
await ws.send(json.dumps(msg))
await asyncio.sleep(repeat_ms / 1000)
else:
await run_suite(args.uri)
def main():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
_setup_sigint(loop)
try:
loop.run_until_complete(main_async())
finally:
try:
loop.run_until_complete(asyncio.sleep(0))
except Exception:
pass
loop.close()
if __name__ == "__main__":
main()

View File

@@ -1,58 +0,0 @@
#!/usr/bin/env python3
"""
N-chase test 1: Red, on=5, off=5, delay=100ms
Runs forever
Run with: mpremote run test/n_chase/1.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting N-Chase Test 1: Red, on=5, off=5, delay=100ms")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=100
)
# Configure test parameters
p.n1 = 5 # On width 5
p.n2 = 5 # Off width 5
p.colors = [(255, 0, 0)] # Red
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"On width: {p.n1}")
print(f"Off width: {p.n2}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("nc")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,58 +0,0 @@
#!/usr/bin/env python3
"""
N-chase test 2: Green, on=10, off=5, delay=150ms
Runs forever
Run with: mpremote run test/n_chase/2.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting N-Chase Test 2: Green, on=10, off=5, delay=150ms")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=150
)
# Configure test parameters
p.n1 = 10 # On width 10
p.n2 = 5 # Off width 5
p.colors = [(0, 255, 0)] # Green
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"On width: {p.n1}")
print(f"Off width: {p.n2}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("nc")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,58 +0,0 @@
#!/usr/bin/env python3
"""
N-chase test 3: Blue, on=3, off=10, delay=80ms
Runs forever
Run with: mpremote run test/n_chase/3.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting N-Chase Test 3: Blue, on=3, off=10, delay=80ms")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=80
)
# Configure test parameters
p.n1 = 3 # On width 3
p.n2 = 10 # Off width 10
p.colors = [(0, 0, 255)] # Blue
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"On width: {p.n1}")
print(f"Off width: {p.n2}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("nc")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,58 +0,0 @@
#!/usr/bin/env python3
"""
N-chase test 4: Yellow, on=7, off=7, delay=120ms
Runs forever
Run with: mpremote run test/n_chase/4.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting N-Chase Test 4: Yellow, on=7, off=7, delay=120ms")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=120
)
# Configure test parameters
p.n1 = 7 # On width 7
p.n2 = 7 # Off width 7
p.colors = [(255, 255, 0)] # Yellow
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"On width: {p.n1}")
print(f"Off width: {p.n2}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("nc")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,58 +0,0 @@
#!/usr/bin/env python3
"""
N-chase test 5: White, on=2, off=8, delay=200ms
Runs forever
Run with: mpremote run test/n_chase/5.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting N-Chase Test 5: White, on=2, off=8, delay=200ms")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=200
)
# Configure test parameters
p.n1 = 2 # On width 2
p.n2 = 8 # Off width 8
p.colors = [(255, 255, 255)] # White
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"On width: {p.n1}")
print(f"Off width: {p.n2}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("nc")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,58 +0,0 @@
#!/usr/bin/env python3
"""
N-chase test 6: Magenta, on=15, off=3, delay=60ms
Runs forever
Run with: mpremote run test/n_chase/6.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting N-Chase Test 6: Magenta, on=15, off=3, delay=60ms")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=60
)
# Configure test parameters
p.n1 = 15 # On width 15
p.n2 = 3 # Off width 3
p.colors = [(255, 0, 255)] # Magenta
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"On width: {p.n1}")
print(f"Off width: {p.n2}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("nc")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,58 +0,0 @@
#!/usr/bin/env python3
"""
N-chase test 7: Cyan, on=4, off=12, delay=180ms
Runs forever
Run with: mpremote run test/n_chase/7.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting N-Chase Test 7: Cyan, on=4, off=12, delay=180ms")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=180
)
# Configure test parameters
p.n1 = 4 # On width 4
p.n2 = 12 # Off width 12
p.colors = [(0, 255, 255)] # Cyan
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"On width: {p.n1}")
print(f"Off width: {p.n2}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("nc")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,58 +0,0 @@
#!/usr/bin/env python3
"""
N-chase test 8: Orange, on=1, off=20, delay=250ms
Runs forever
Run with: mpremote run test/n_chase/8.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting N-Chase Test 8: Orange, on=1, off=20, delay=250ms")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=250
)
# Configure test parameters
p.n1 = 1 # On width 1
p.n2 = 20 # Off width 20
p.colors = [(255, 128, 0)] # Orange
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"On width: {p.n1}")
print(f"Off width: {p.n2}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("nc")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,62 +0,0 @@
#!/usr/bin/env python3
"""
N-chase test 9: Cyan, on=20, off=20, delay=200ms
Runs forever
Run with: mpremote run test/n_chase/9.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting N-Chase Test 9: Cyan, on=20, off=20, delay=200ms")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=200
)
# Configure test parameters
p.n1 = 20 # On width 20
p.n2 = 20 # Off width 20
p.n3 = 20 # Reserved for future use
p.n4 = -5 # Reserved for future use
p.colors = [(0, 255, 255)] # Cyan
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"On width: {p.n1}")
print(f"Off width: {p.n2}")
print(f"n3: {p.n3}")
print(f"n4: {p.n4}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("nc")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,56 +0,0 @@
#!/usr/bin/env python3
"""
Rainbow test 1: Single node (full spectrum), 3 second cycle, full brightness
Runs forever
Run with: mpremote run test/rainbow/1.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Rainbow Test 1: Single node (full spectrum), 3 second cycle")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=3000 # 3 second cycle
)
# Configure test parameters
p.n1 = 1 # 1 node (full spectrum)
p.delay = 3000 # 3 second cycle
p.brightness = 255 # Full brightness
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Nodes: {p.n1}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("rb")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,56 +0,0 @@
#!/usr/bin/env python3
"""
Rainbow test 2: Two nodes, 2 second cycle, full brightness
Runs forever
Run with: mpremote run test/rainbow/2.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Rainbow Test 2: Two nodes, 2 second cycle")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=2000 # 2 second cycle
)
# Configure test parameters
p.n1 = 2 # 2 nodes
p.delay = 2000 # 2 second cycle
p.brightness = 255 # Full brightness
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Nodes: {p.n1}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("rb")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,56 +0,0 @@
#!/usr/bin/env python3
"""
Rainbow test 3: Three nodes (RGB), 1.5 second cycle, full brightness
Runs forever
Run with: mpremote run test/rainbow/3.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Rainbow Test 3: Three nodes (RGB), 1.5 second cycle")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=1500 # 1.5 second cycle
)
# Configure test parameters
p.n1 = 3 # 3 nodes
p.delay = 1500 # 1.5 second cycle
p.brightness = 255 # Full brightness
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Nodes: {p.n1}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("rb")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,56 +0,0 @@
#!/usr/bin/env python3
"""
Rainbow test 4: Four nodes (RYGB), 1 second cycle, full brightness
Runs forever
Run with: mpremote run test/rainbow/4.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Rainbow Test 4: Four nodes (RYGB), 1 second cycle")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=1000 # 1 second cycle
)
# Configure test parameters
p.n1 = 4 # 4 nodes
p.delay = 1000 # 1 second cycle
p.brightness = 255 # Full brightness
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Nodes: {p.n1}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("rb")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,56 +0,0 @@
#!/usr/bin/env python3
"""
Rainbow test 5: Six nodes (ROYGBP), 0.8 second cycle, full brightness
Runs forever
Run with: mpremote run test/rainbow/5.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Rainbow Test 5: Six nodes (ROYGBP), 0.8 second cycle")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=800 # 0.8 second cycle
)
# Configure test parameters
p.n1 = 6 # 6 nodes
p.delay = 800 # 0.8 second cycle
p.brightness = 255 # Full brightness
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Nodes: {p.n1}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("rb")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,56 +0,0 @@
#!/usr/bin/env python3
"""
Rainbow test 6: Eight nodes, 0.6 second cycle, full brightness
Runs forever
Run with: mpremote run test/rainbow/6.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Rainbow Test 6: Eight nodes, 0.6 second cycle")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=600 # 0.6 second cycle
)
# Configure test parameters
p.n1 = 8 # 8 nodes
p.delay = 600 # 0.6 second cycle
p.brightness = 255 # Full brightness
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Nodes: {p.n1}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("rb")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,56 +0,0 @@
#!/usr/bin/env python3
"""
Rainbow test 7: Twelve nodes (fine gradation), 0.4 second cycle, full brightness
Runs forever
Run with: mpremote run test/rainbow/7.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Rainbow Test 7: Twelve nodes (fine gradation), 0.4 second cycle")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=400 # 0.4 second cycle
)
# Configure test parameters
p.n1 = 12 # 12 nodes
p.delay = 400 # 0.4 second cycle
p.brightness = 255 # Full brightness
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Nodes: {p.n1}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("rb")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,56 +0,0 @@
#!/usr/bin/env python3
"""
Rainbow test 8: Many nodes (20), slow cycle (2 seconds), full brightness
Runs forever
Run with: mpremote run test/rainbow/8.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Rainbow Test 8: Many nodes (20), slow cycle (2 seconds)")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=2000 # 2 second cycle
)
# Configure test parameters
p.n1 = 20 # 20 nodes
p.delay = 2000 # 2 second cycle
p.brightness = 255 # Full brightness
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Nodes: {p.n1}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("rb")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,56 +0,0 @@
#!/usr/bin/env python3
"""
Rainbow test 9: Five nodes, 1 second cycle, full brightness
Runs forever
Run with: mpremote run test/rainbow/9.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Rainbow Test 9: Five nodes, 1 second cycle")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=1000 # 1 second cycle
)
# Configure test parameters
p.n1 = 5 # 5 nodes
p.delay = 1000 # 1 second cycle
p.brightness = 255 # Full brightness
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Nodes: {p.n1}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("rb")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,100 +0,0 @@
#!/usr/bin/env python3
"""
Rainbow test suite - runs all tests in sequence forever
Run with: mpremote run test/rainbow/main.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
def run_test_case(p, test_name, n1, delay, brightness, duration_ms=6000):
"""Run a test case for specified duration"""
print(f"\n--- {test_name} ---")
print(f"Parameters: n1={n1} nodes, delay={delay}ms cycle time, brightness={brightness}")
# Configure parameters
p.n1 = n1
p.delay = delay
p.brightness = brightness
# Restart the pattern with new parameters
p.run = False
while p.running:
utime.sleep_ms(1)
p.select("rb")
# Run for specified duration
start_time = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start_time) < duration_ms:
wdt.feed()
utime.sleep_ms(100)
# Stop pattern
p.run = False
print(f"{test_name} completed")
# Brief pause between tests
utime.sleep_ms(500)
def test_rainbow_suite():
"""Run all rainbow tests continuously"""
print("Starting rainbow test suite...")
print("Runs all tests in sequence, then repeats")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=1000
)
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Base Brightness: {p.brightness}")
# Initialize watchdog timer
global wdt
wdt = WDT(timeout=10000)
wdt.feed()
# Test configurations
tests = [
("Test 1: Single node (full spectrum)", 1, 3000, 255, 12000),
("Test 2: Two nodes", 2, 2000, 255, 10000),
("Test 3: Three nodes (RGB)", 3, 1500, 255, 9000),
("Test 4: Four nodes (RYGB)", 4, 1000, 255, 8000),
("Test 5: Six nodes (ROYGBP)", 6, 800, 255, 8000),
("Test 6: Eight nodes", 8, 600, 255, 6000),
("Test 7: Twelve nodes (fine gradation)", 12, 400, 255, 6000),
("Test 8: Many nodes (20), slow cycle", 20, 2000, 255, 10000),
("Test 9: Five nodes", 5, 1000, 255, 6000),
]
cycle = 0
try:
while True:
cycle += 1
print(f"\n\n========== Starting Cycle {cycle} ==========")
for test_name, n1, delay, brightness, duration in tests:
run_test_case(p, test_name, n1, delay, brightness, duration)
print("\n========== Cycle completed ==========\n")
except KeyboardInterrupt:
print("\n\nStopping test suite...")
p.run = False
p.off()
print("LEDs turned off")
if __name__ == "__main__":
test_rainbow_suite()

View File

@@ -1,160 +0,0 @@
#!/usr/bin/env python3
"""
Random test - randomly selects and runs test patterns for 2-10 minutes each
Run with: mpremote run test/random_test.py
"""
import patterns
import utime
import random
import math
from settings import Settings
from machine import WDT
def run_pattern(p, pattern_name, config, duration_ms):
"""Run a pattern for specified duration"""
print(f"\n{'='*60}")
print(f"Starting: {pattern_name}")
print(f"Duration: {duration_ms//1000} seconds")
# Configure parameters
if pattern_name == "blink":
p.delay = config.get("delay", 500)
elif pattern_name == "circle_loading":
p.n1 = config.get("n1", 50)
p.n2 = config.get("n2", 100)
p.n3 = config.get("n3", 200)
p.n4 = config.get("n4", 0)
p.delay = config.get("delay", 2000)
elif pattern_name == "sine_brightness":
p.n1 = config.get("n1", 50)
p.brightness = config.get("brightness", 255)
p.delay = config.get("delay", 1000)
elif pattern_name == "rainbow":
p.n1 = config.get("n1", 1)
p.delay = config.get("delay", 1000)
elif pattern_name == "flicker":
p.n1 = config.get("n1", 50)
p.delay = config.get("delay", 50)
elif pattern_name == "n_chase":
p.n1 = config.get("n1", 5)
p.n2 = config.get("n2", 5)
p.n3 = config.get("n3", 1)
p.n4 = config.get("n4", 1)
p.delay = config.get("delay", 100)
# Set color if provided
if "color" in config:
p.colors = [config["color"]]
# Start pattern
p.select(config.get("short_name", "o"))
# Run for duration
start_time = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start_time) < duration_ms:
wdt.feed()
utime.sleep_ms(100)
# Stop pattern
p.run = False
while p.running:
utime.sleep_ms(1)
p.off()
print(f"Completed: {pattern_name}")
utime.sleep_ms(500)
def random_test():
"""Run random test patterns continuously"""
print("Starting Random Test Suite")
print("Patterns will change every 1-5 minutes")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255
)
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
# Initialize watchdog
global wdt
wdt = WDT(timeout=10000)
wdt.feed()
# Define all test configurations
test_configs = [
# Circle loading patterns
{"pattern": "circle_loading", "short_name": "cl", "n1": 50, "n2": 100, "n3": 200, "n4": 0, "delay": 2000, "color": (255, 0, 0)},
{"pattern": "circle_loading", "short_name": "cl", "n1": 30, "n2": 60, "n3": 100, "n4": 0, "delay": 1500, "color": (0, 255, 0)},
{"pattern": "circle_loading", "short_name": "cl", "n1": 75, "n2": 150, "n3": 300, "n4": 10, "delay": 3000, "color": (0, 0, 255)},
# Sine brightness patterns
{"pattern": "sine_brightness", "short_name": "sb", "n1": 50, "brightness": 255, "delay": 1000, "color": (255, 0, 0)},
{"pattern": "sine_brightness", "short_name": "sb", "n1": 100, "brightness": 255, "delay": 800, "color": (0, 255, 0)},
{"pattern": "sine_brightness", "short_name": "sb", "n1": 0, "brightness": 255, "delay": 1500, "color": (0, 0, 255)},
# Rainbow patterns
{"pattern": "rainbow", "short_name": "rb", "n1": 1, "delay": 3000, "color": None},
{"pattern": "rainbow", "short_name": "rb", "n1": 2, "delay": 2000, "color": None},
{"pattern": "rainbow", "short_name": "rb", "n1": 3, "delay": 1500, "color": None},
{"pattern": "rainbow", "short_name": "rb", "n1": 6, "delay": 1000, "color": None},
{"pattern": "rainbow", "short_name": "rb", "n1": 12, "delay": 800, "color": None},
# Flicker patterns
{"pattern": "flicker", "short_name": "fl", "n1": 50, "delay": 50, "color": (255, 0, 0)},
{"pattern": "flicker", "short_name": "fl", "n1": 30, "delay": 100, "color": (0, 255, 0)},
{"pattern": "flicker", "short_name": "fl", "n1": 100, "delay": 30, "color": (0, 0, 255)},
# N-chase patterns (mix of forward-only and bidirectional)
# Forward-only patterns
{"pattern": "n_chase", "short_name": "nc", "n1": 5, "n2": 5, "n3": 5, "n4": 5, "delay": 100, "color": (255, 0, 0)},
{"pattern": "n_chase", "short_name": "nc", "n1": 10, "n2": 5, "n3": 10, "n4": 10, "delay": 150, "color": (0, 255, 0)},
{"pattern": "n_chase", "short_name": "nc", "n1": 20, "n2": 20, "n3": 20, "n4": 20, "delay": 200, "color": (0, 255, 255)},
# Bidirectional patterns
{"pattern": "n_chase", "short_name": "nc", "n1": 15, "n2": 10, "n3": 10, "n4": 5, "delay": 180, "color": (255, 255, 0)},
{"pattern": "n_chase", "short_name": "nc", "n1": 20, "n2": 20, "n3": 20, "n4": 5, "delay": 200, "color": (255, 0, 255)},
{"pattern": "n_chase", "short_name": "nc", "n1": 12, "n2": 8, "n3": 15, "n4": 8, "delay": 160, "color": (255, 128, 128)},
# N-chase patterns (forward only, large segments)
{"pattern": "n_chase", "short_name": "nc", "n1": 30, "n2": 10, "n3": 5, "n4": 5, "delay": 150, "color": (255, 128, 0)},
{"pattern": "n_chase", "short_name": "nc", "n1": 40, "n2": 20, "n3": 10, "n4": 10, "delay": 180, "color": (128, 255, 0)},
{"pattern": "n_chase", "short_name": "nc", "n1": 50, "n2": 25, "n3": 20, "n4": 20, "delay": 200, "color": (0, 128, 255)},
{"pattern": "n_chase", "short_name": "nc", "n1": 60, "n2": 30, "n3": 30, "n4": 30, "delay": 220, "color": (255, 0, 128)},
{"pattern": "n_chase", "short_name": "nc", "n1": 80, "n2": 40, "n3": 50, "n4": 50, "delay": 250, "color": (128, 0, 255)},
{"pattern": "n_chase", "short_name": "nc", "n1": 100, "n2": 50, "n3": 100, "n4": 100, "delay": 300, "color": (255, 255, 255)},
]
try:
cycle = 0
while True:
cycle += 1
print(f"\n\n{'='*60}")
print(f"Random Test Cycle {cycle}")
print(f"{'='*60}")
# Randomly select a test configuration
config = random.choice(test_configs)
# Random duration between 1-5 minutes (60,000 - 300,000 ms)
duration_ms = random.randint(60000, 300000)
# Run the selected pattern
run_pattern(p, config["pattern"], config, duration_ms)
except KeyboardInterrupt:
print("\n\nStopping random test suite...")
p.run = False
p.off()
print("LEDs turned off")
if __name__ == "__main__":
random_test()

View File

@@ -1,120 +0,0 @@
#!/usr/bin/env python3
"""
Test script for sine brightness pattern
Run with: mpremote run test/sine_brightness.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
def run_test_case(p, test_name, duration_ms=5000):
"""Run a test case for specified duration"""
print(f"\n--- {test_name} ---")
print(f"Parameters: n1={p.n1} min brightness, brightness={p.brightness} max brightness, delay={p.delay}ms wavelength")
print(f"Color: {p.colors[0]}")
print(f"Running for {duration_ms//1000} seconds...")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("sb")
# Run for specified duration
start_time = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start_time) < duration_ms:
wdt.feed()
utime.sleep_ms(100)
# Stop pattern
p.run = False
print(f"{test_name} completed")
# Brief pause between tests
utime.sleep_ms(500)
def test_sine_brightness():
print("Testing sine brightness pattern with multiple configurations...")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255, # Full brightness
delay=1000 # Base wavelength
)
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Base Brightness: {p.brightness}")
# Test Case 1: Slow breathing
p.n1 = 50 # Min brightness 50
p.brightness = 255 # Max brightness 255
p.delay = 3000 # 3 second cycle
p.colors = [(255, 0, 0)] # Red
run_test_case(p, "Test 1: Slow breathing (50-255 brightness)", 12000)
# Test Case 2: Fast pulsing
p.n1 = 100 # Min brightness 100
p.brightness = 255 # Max brightness 255
p.delay = 500 # 0.5 second cycle
p.colors = [(0, 255, 0)] # Green
run_test_case(p, "Test 2: Fast pulsing (100-255 brightness)", 8000)
# Test Case 3: Medium breathing
p.n1 = 80 # Min brightness 80
p.brightness = 255 # Max brightness 255
p.delay = 1500 # 1.5 second cycle
p.colors = [(0, 0, 255)] # Blue
run_test_case(p, "Test 3: Medium breathing (80-255 brightness)", 10000)
# Test Case 4: Very slow breathing
p.n1 = 150 # Min brightness 150
p.brightness = 255 # Max brightness 255
p.delay = 5000 # 5 second cycle
p.colors = [(255, 255, 0)] # Yellow
run_test_case(p, "Test 4: Very slow breathing (150-255 brightness)", 15000)
# Test Case 5: Very fast pulsing
p.n1 = 50 # Min brightness 50
p.brightness = 255 # Max brightness 255
p.delay = 200 # 0.2 second cycle
p.colors = [(255, 0, 255)] # Magenta
run_test_case(p, "Test 5: Very fast pulsing (50-255 brightness)", 6000)
# Test Case 6: Low range, slow cycle
p.n1 = 200 # Min brightness 200
p.brightness = 255 # Max brightness 255
p.delay = 4000 # 4 second cycle
p.colors = [(0, 255, 255)] # Cyan
run_test_case(p, "Test 6: Low range, slow cycle (200-255 brightness)", 12000)
# Test Case 7: High range, medium cycle
p.n1 = 100 # Min brightness 100
p.brightness = 255 # Max brightness 255
p.delay = 1000 # 1 second cycle
p.colors = [(255, 128, 0)] # Orange
run_test_case(p, "Test 7: High range, medium cycle (100-255 brightness)", 8000)
# Test Case 8: Ultra fast strobe
p.n1 = 100 # Min brightness 100
p.brightness = 255 # Max brightness 255
p.delay = 100 # 0.1 second cycle
p.colors = [(128, 0, 255)] # Purple
run_test_case(p, "Test 8: Ultra fast strobe (100-255 brightness)", 4000)
print("\n=== All tests completed ===")
# Turn off LEDs
p.off()
print("LEDs turned off")
if __name__ == "__main__":
test_sine_brightness()