Compare commits
20 Commits
patterns-a
...
espnow
Author | SHA1 | Date | |
---|---|---|---|
355d113e32 | |||
d715af4344 | |||
67c4a1a6f6 | |||
748ad4b507 | |||
1275d60aaa | |||
d8e853183b | |||
8cfb3e156b | |||
d599af271b | |||
93560a253e | |||
d68817ea18 | |||
a7a2274a59 | |||
df838dc4d6 | |||
4ec48b9f8f | |||
1456ed8a6e | |||
80d5a66fab | |||
44cb35d1aa | |||
fc080f7796 | |||
70fe5a0cdc | |||
2a7b5527a5 | |||
50545e3170 |
71
8_BAR_SETUP.md
Normal file
71
8_BAR_SETUP.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# 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
|
5
Pipfile
5
Pipfile
@@ -7,8 +7,13 @@ name = "pypi"
|
||||
mpremote = "*"
|
||||
pyserial = "*"
|
||||
esptool = "*"
|
||||
watchfiles = "*"
|
||||
uvicorn = "*"
|
||||
|
||||
[dev-packages]
|
||||
|
||||
[requires]
|
||||
python_version = "3.12"
|
||||
|
||||
[scripts]
|
||||
dev = 'watchfiles "./dev.py /dev/ttyACM0 src reset follow"'
|
||||
|
747
Pipfile.lock
generated
747
Pipfile.lock
generated
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "8b14bb293b7e7117ffc89c2bc92d7aa2290e8f68be7fc0f073f2b3f7f959ef71"
|
||||
"sha256": "53809b70ded7a2b3e577a8a4263fbadbb722d1e8d92eb016e134b0776fd40f6b"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@@ -16,152 +16,152 @@
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"argcomplete": {
|
||||
"anyio": {
|
||||
"hashes": [
|
||||
"sha256:65b3133a29ad53fb42c48cf5114752c7ab66c1c38544fdf6460f450c09b42591",
|
||||
"sha256:d0519b1bc867f5f4f4713c41ad0aba73a4a5f007449716b16f385f2166dc6adf"
|
||||
"sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6",
|
||||
"sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1"
|
||||
],
|
||||
"markers": "sys_platform != 'win32'",
|
||||
"version": "==3.6.2"
|
||||
"markers": "python_version >= '3.9'",
|
||||
"version": "==4.10.0"
|
||||
},
|
||||
"bitarray": {
|
||||
"hashes": [
|
||||
"sha256:01299fb36af3e7955967f3dbc4097a2d88845166837899350f411d95a857f8aa",
|
||||
"sha256:0580b905ad589e3be52d36fbc83d32f6e3f6a63751d6c0da0ca328c32d037790",
|
||||
"sha256:0952d05e1d6b0a736d73d34128b652d7549ba7d00ccc1e7c00efbc6edd687ee3",
|
||||
"sha256:0b7e1f4139d3f17feba72e386a8f1318fb35182ff65890281e727fd07fdfbd72",
|
||||
"sha256:0ba347a4dcc990637aa700227675d8033f68b417dcd7ccf660bd2e87e10885ec",
|
||||
"sha256:0d11e1a8914321fac34f50c48a9b1f92a1f51f45f9beb23e990806588137c4ca",
|
||||
"sha256:131ff1eed8902fb54ea64f8d0bf8fcbbda8ad6b9639d81cacc3a398c7488fecb",
|
||||
"sha256:14f04e4eec65891523a8ca3bf9e1dcdefed52d695f40c4e50d5980471ffd22a4",
|
||||
"sha256:176991b2769425341da4d52a684795498c0cd4136f4329ba9d524bcb96d26604",
|
||||
"sha256:1b7e89d4005eee831dc90d50c69af74ece6088f3c1b673d0089c8ef7d5346c37",
|
||||
"sha256:1ce3e352f1b7f1201b04600f93035312b00c9f8f4d606048c39adac32b2fb738",
|
||||
"sha256:24296caffe89af65fc8029a56274db6a268f6a297a5163e65df8177c2dd67b19",
|
||||
"sha256:2441da551787086c57fa8983d43e103fd2519389c8e03302908697138c287d6a",
|
||||
"sha256:26a26614bba95f3e4ea8c285206a4efe5ffb99e8539356d78a62491facc326cf",
|
||||
"sha256:28d866fa462d77cafbf284aea14102a31dcfdebb9a5abbfb453f6eb6b2deb4fd",
|
||||
"sha256:2e92d2d7d405e004f2bdf9ff6d58faed6d04e0b74a9d96905ade61c293abe315",
|
||||
"sha256:2fbd399cfdb7dee0bb4705bc8cd51163a9b2f25bb266807d57e5c693e0a14df2",
|
||||
"sha256:307e4cd6b94de4b4b5b0f4599ffddabde4c33ac22a74998887048d24cb379ad3",
|
||||
"sha256:31f21c7df3b40db541182db500f96cf2b9688261baec7b03a6010fdfc5e31855",
|
||||
"sha256:36851e3244950adc75670354dcd9bcad65e1695933c18762bb6f7590734c14ef",
|
||||
"sha256:39fdd56fd9076a4a34c3cd21e1c84dc861dac5e92c1ed9daed6aed6b11719c8c",
|
||||
"sha256:3afe39028afff6e94bb90eb0f8c5eb9357c0e37ce3c249f96dbcfc1a73938015",
|
||||
"sha256:3fcdaf79970b41cfe21b6cf6a7bbe2d0f17e3371a4d839f1279283ac03dd2a47",
|
||||
"sha256:421da43706c9a01d1b1454c34edbff372a7cfeff33879b6c048fc5f4481a9454",
|
||||
"sha256:42376c9e0a1357acc8830c4c0267e1c30ebd04b2d822af702044962a9f30b795",
|
||||
"sha256:434180c1340268763439b80d21e074df24633c8748a867573bafecdbfaa68a76",
|
||||
"sha256:434e389958ab98415ed4d9d67dd94c0ac835036a16b488df6736222f4f55ff35",
|
||||
"sha256:47abbec73f20176e119f5c4c68aaf243c46a5e072b9c182f2c110b5b227256a7",
|
||||
"sha256:492524a28c3aab6a4ef0a741ee9f3578b6606bb52a7a94106c386bdebab1df44",
|
||||
"sha256:4bb2fa914a7bbcd7c6a457d44461a8540b9450e9bb4163d734eb74bffba90e69",
|
||||
"sha256:4bda4e4219c6271beec737a5361b009dcf9ff6d84a2df92bf3dd4f4e97bb87e5",
|
||||
"sha256:4c516daf790bd870d7575ac0e4136f1c3bc180b0de2a6bfa9fa112ea668131a0",
|
||||
"sha256:4ddef0b620db43dfde43fe17448ddc37289f67ad9a8ae39ffa64fa7bf529145f",
|
||||
"sha256:50da5ecd86ee25df9f658d8724efbe8060de97217fb12a1163bee61d42946d83",
|
||||
"sha256:50df8e1915a1acfd9cd0a4657d26cacd5aee4c3286ebb63e9dd75271ea6b54e0",
|
||||
"sha256:518e04584654a155fca829a6fe847cd403a17007e5afdc2b05b4240b53cd0842",
|
||||
"sha256:51ce410a2d91da4b98d0f043df9e0938c33a2d9ad4a370fa8ec1ce7352fc20d9",
|
||||
"sha256:52e8d36933bb3fb132c95c43171f47f07c22dd31536495be20f86ddbf383e3c6",
|
||||
"sha256:52edf707f2fddb6a60a20093c3051c1925830d8c4e7fb2692aac2ee970cee2b0",
|
||||
"sha256:535cc398610ff22dc0341e8833c34be73634a9a0a5d04912b4044e91dfbbc413",
|
||||
"sha256:54093229fec0f8c605b7873020c07681c1f1f96c433ae082d2da106ab11b206f",
|
||||
"sha256:54ac6f8d2f696d83f9ccbb4cc4ce321dc80b9fa4613749a8ab23bda5674510ea",
|
||||
"sha256:5500052aaf761afede3763434097a59042e22fbde508c88238d34105c13564c0",
|
||||
"sha256:551844744d22fe2e37525bd7132d2e9dae5a9621e3d8a43f46bbe6edadb4c63b",
|
||||
"sha256:58365c6c3e4a5ebbc8f28bf7764f5b00be5c8b1ffbd70474e6f801383f3fe0a0",
|
||||
"sha256:5aacbf54ad69248e17aab92a9f2d8a0a7efaea9d5401207cb9dac41d46294d56",
|
||||
"sha256:5d47d349468177afbe77e5306e70fd131d8da6946dd22ed93cbe70c5f2965307",
|
||||
"sha256:5dcb5aaaa2d91cc04fa9adfe31222ab150e72d99c779b1ddca10400a2fd319ec",
|
||||
"sha256:601fedd0e5227a5591e2eae2d35d45a07f030783fc41fd217cdf0c74db554cb9",
|
||||
"sha256:62c2278763edc823e79b8f0a0fdc7c8c9c45a3e982db9355042839c1f0c4ea92",
|
||||
"sha256:638ad50ecbffd05efdfa9f77b24b497b8e523f078315846614c647ebc3389bb5",
|
||||
"sha256:64a5404a258ef903db67d7911147febf112858ba30c180dae0c23405412e0a2f",
|
||||
"sha256:64cef9f2d15261ea667838a4460f75acf4b03d64d53df664357541cc8d2c8183",
|
||||
"sha256:653d56c58197940f0c1305cb474b75597421b424be99284915bb4f3529d51837",
|
||||
"sha256:673a21ebb6c72904d7de58fe8c557bad614fce773f21ddc86bcf8dd09a387a32",
|
||||
"sha256:69679fcd5f2c4b7c8920d2824519e3bff81a18fac25acf33ded4524ea68d8a39",
|
||||
"sha256:71838052ad546da110b8a8aaa254bda2e162e65af563d92b15c8bc7ab1642909",
|
||||
"sha256:7445c34e5d55ec512447efa746f046ecf4627c08281fc6e9ef844423167237bc",
|
||||
"sha256:751a2cd05326e1552b56090595ba8d35fe6fef666d5ca9c0a26d329c65a9c4a0",
|
||||
"sha256:75eb4d353dcf571d98e2818119af303fb0181b54361ac9a3e418b31c08131e56",
|
||||
"sha256:76abaeac4f94eda1755eed633a720c1f5f90048cb7ea4ab217ea84c48414189a",
|
||||
"sha256:78d069a00a8d06fb68248edd5bf2aa5e8009f4f5eae8dd5b5a529812132ad8a6",
|
||||
"sha256:7964b17923c1bfa519afe273335023e0800c64bdca854008e75f2b148614d3f2",
|
||||
"sha256:7c7913d3cf7017bd693177ca0a4262d51587378d9c4ae38d13be3655386f0c27",
|
||||
"sha256:8076650a08cec080f6726860c769320c27eb4379cfd22e2f5732787dec119bfe",
|
||||
"sha256:811f559e0e5fca85d26b834e02f2a767aa7765e6b1529d4b2f9d4e9015885b4b",
|
||||
"sha256:824bd92e53f8e32dfa4bf38643246d1a500b13461ade361d342a8fcc3ddb6905",
|
||||
"sha256:833e06b01ff8f5a9f5b52156a23e9930402d964c96130f6d0bd5297e5dec95dc",
|
||||
"sha256:8360759897d50e4f7ec8be51f788119bd43a61b1fe9c68a508a7ba495144859a",
|
||||
"sha256:8627fc0c9806d6dac2fb422d9cd650b0d225f498601381d334685b9f071b793c",
|
||||
"sha256:86dd5b8031d690afc90430997187a4fc5871bc6b81d73055354b8eb48b3e6342",
|
||||
"sha256:8c84c3df9b921439189d0be6ad4f4212085155813475a58fbc5fb3f1d5e8a001",
|
||||
"sha256:8c89219a672d0a15ab70f8a6f41bc8355296ec26becef89a127c1a32bb2e6345",
|
||||
"sha256:8f267edd51db6903c67b2a2b0f780bb0e52d2e92ec569ddd241486eeff347283",
|
||||
"sha256:90178b8c6f75b43612dadf50ff0df08a560e220424ce33cf6d2514d7ab1803a7",
|
||||
"sha256:90b35553c318b49d5ffdaf3d25b6f0117fd5bbfc3be5576fc41ca506ca0e9b8e",
|
||||
"sha256:9101d48f9532ceb6b1d6a5f7d3a2dd5c853015850c65a47045c70f5f2f9ff88f",
|
||||
"sha256:946e97712014784c3257e4ca45cf5071ffdbbebe83977d429e8f7329d0e2387f",
|
||||
"sha256:9511420cf727eb6e603dc6f3c122da1a16af38abc92272a715ce68c47b19b140",
|
||||
"sha256:958b75f26f8abbcb9bc47a8a546a0449ba565d6aac819e5bb80417b93e5777fa",
|
||||
"sha256:99ea63932e86b08e36d6246ff8f663728a5baefa7e9a0e2f682864fe13297514",
|
||||
"sha256:9aa5cf7a6a8597968ff6f4e7488d5518bba911344b32b7948012a41ca3ae7e41",
|
||||
"sha256:9c8f580590822df5675b9bc04b9df534be23a4917f709f9483fa554fd2e0a4df",
|
||||
"sha256:9ce64e247af33fa348694dbf7f4943a60040b5cc04df813649cc8b54c7f54061",
|
||||
"sha256:9d6fe373572b20adde2d6a58f8dc900b0cb4eec625b05ca1adbf053772723c78",
|
||||
"sha256:a0c87ffc5bf3669b0dfa91752653c41c9c38e1fd5b95aeb4c7ee40208c953fcd",
|
||||
"sha256:a1adc8cd484de52b6b11a0e59e087cd3ae593ce4c822c18d4095d16e06e49453",
|
||||
"sha256:a29ad824bf4b735cb119e2c79a4b821ad462aeb4495e80ff186f1a8e48362082",
|
||||
"sha256:ab52dd26d24061d67f485f3400cc7d3d5696f0246294a372ef09aa8ef31a44c4",
|
||||
"sha256:ac5d80cd43a9a995a501b4e3b38802628b35065e896f79d33430989e2e3f0870",
|
||||
"sha256:af6a09c296aa2d68b25eb154079abd5a58da883db179e9df0fc9215c405be6be",
|
||||
"sha256:b0bca424ee4d80a4880da332e56d2863e8d75305842c10aa6e94eb975bcad4fc",
|
||||
"sha256:b521c2d73f6fa1c461a68c5d220836d0fea9261d5f934833aaffde5114aecffb",
|
||||
"sha256:b77a03aba84bf2d2c8f2d5a81af5957da42324d9f701d584236dc735b6a191f8",
|
||||
"sha256:b81664adf97f54cb174472f5511075bfb5e8fb13151e9c1592a09b45d544dab0",
|
||||
"sha256:b9f2247b76e2e8c88f81fb850adb211d9b322f498ae7e5797f7629954f5b9767",
|
||||
"sha256:bf7ead8b947a14c785d04943ff4743db90b0c40a4cb27e6bef4c3650800a927d",
|
||||
"sha256:c001b7ac2d9cf1a73899cf857d3d66919deca677df26df905852039c46aa30a6",
|
||||
"sha256:c00b2ea9aab5b2c623b1901a4c04043fb847c8bd64a2f52518488434eb44c4e6",
|
||||
"sha256:c17eae957d61fea05d3f2333a95dd79dc4733f3eadf44862cd6d586daae31ea3",
|
||||
"sha256:c17fd3a63b31a21a979962bd3ab0f96d22dcdb79dc5149efc2cf66a16ae0bb59",
|
||||
"sha256:cb388586c9b4d338f9585885a6f4bd2736d4a7a7eb4b63746587cb8d04f7d156",
|
||||
"sha256:cbf063667ef89b0d8b8bd1fcaaa4dcc8c65c17048eb14fb1fa9dbe9cb5197c81",
|
||||
"sha256:d030b96f6ccfec0812e2fc1b02ab72d56a408ec215f496a7a25cde31160a88b4",
|
||||
"sha256:d2c411b7d3784109dfc33f5f7cdf331d3373b8349a4ad608ee482f1a04c30efe",
|
||||
"sha256:d2c8b7da269eb877cc2361d868fdcb63bfe7b5821c5b3ea2640be3f4b047b4bb",
|
||||
"sha256:d3f5cec4f8d27284f559a0d7c4a4bdfbae74d3b69d09c3f3b53989a730833ad8",
|
||||
"sha256:d40dbc3609f1471ca3c189815ab4596adae75d8ee0da01412b2e3d0f6e94ab46",
|
||||
"sha256:d57b3b92bfa453cba737716680292afb313ec92ada6c139847e005f5ac1ad08c",
|
||||
"sha256:da225a602cb4a97900e416059bc77d7b0bb8ac5cb6cb3cc734fd01c636387d2b",
|
||||
"sha256:dc448e4871fc4df22dd04db4a7b34829e5c3404003b9b1709b6b496d340db9c7",
|
||||
"sha256:dc6407e899fc3148d796fc4c3b0cec78153f034c5ff9baa6ae9c91d7ea05fb45",
|
||||
"sha256:dd0ba0cc46b9a7d5cee4c4a9733dce2f0aa21caf04fe18d18d2025a4211adc18",
|
||||
"sha256:dea204d3c6ec17fc3084c1db11bcad1347f707b7f5c08664e116a9c75ca134e9",
|
||||
"sha256:e362fc7a72fd00f641b3d6ed91076174cae36f49183afe8b4b4b77a2b5a116b0",
|
||||
"sha256:e44be933a60b27ef0378a2fdc111ae4ac53a090169db9f97219910cac51ff885",
|
||||
"sha256:e61b7552c953e58cf2d82b95843ca410eef18af2a5380f3ff058d21eaf902eda",
|
||||
"sha256:e75c4a1f00f46057f2fc98d717b2eabba09582422fe608158beed2ef0a5642da",
|
||||
"sha256:e95f13d615f91da5a5ee5a782d9041c58be051661843416f2df9453f57008d40",
|
||||
"sha256:e9b18889a809d8f190e09dd6ee513983e1cdc04c3f23395d237ccf699dce5eaf",
|
||||
"sha256:ea48f168274d60f900f847dd5fff9bd9d4e4f8af5a84149037c2b5fe1712fa0b",
|
||||
"sha256:ed6f9b158c11e7bcf9b0b6788003aed5046a0759e7b25e224d9551a01c779ee7",
|
||||
"sha256:eeda85d034a2649b7e4dbd7067411e9c55c1fc65fafb9feb973d810b103e36a0",
|
||||
"sha256:f1767c325ef4983f52a9d62590f09ea998c06d8d4aa9f13b9eeabaac3536381e",
|
||||
"sha256:f26f3197779fe5a90a54505334d34ceb948cec6378caea49cd9153b3bbe57566",
|
||||
"sha256:f46e7fe734b57f3783a324bf3a7053df54299653e646d86558a4b2576cb47208",
|
||||
"sha256:f4e2fc0f6a573979462786edbf233fc9e1b644b4e790e8c29796f96bbe45353a",
|
||||
"sha256:f51322a55687f1ac075b897d409d0314a81f1ec55ebae96eeca40c9e8ad4a1c1",
|
||||
"sha256:f5f44d71486949237679a8052cda171244d0be9279776c1d3d276861950dd608",
|
||||
"sha256:f62738cc16a387aa2f0dc6e93e0b0f48d5b084db249f632a0e3048d5ace783e6",
|
||||
"sha256:f7cee295219988b50b543791570b013e3f3325867f9650f6233b48cb00b020c2",
|
||||
"sha256:f7eb851d62a3166b8d1da5d5740509e215fa5b986467bf135a5a2d197bf16345",
|
||||
"sha256:f937ef83e5666b6266236f59b1f38abe64851fb20e7d8d13033c5168d35ef39d",
|
||||
"sha256:fd3ed1f7d2d33856252863d5fa976c41013fac4eb0898bf7c3f5341f7ae73e06"
|
||||
"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"
|
||||
],
|
||||
"version": "==3.3.1"
|
||||
"version": "==3.7.1"
|
||||
},
|
||||
"bitstring": {
|
||||
"hashes": [
|
||||
@@ -173,132 +173,168 @@
|
||||
},
|
||||
"cffi": {
|
||||
"hashes": [
|
||||
"sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8",
|
||||
"sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2",
|
||||
"sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1",
|
||||
"sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15",
|
||||
"sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36",
|
||||
"sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824",
|
||||
"sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8",
|
||||
"sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36",
|
||||
"sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17",
|
||||
"sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf",
|
||||
"sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc",
|
||||
"sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3",
|
||||
"sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed",
|
||||
"sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702",
|
||||
"sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1",
|
||||
"sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8",
|
||||
"sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903",
|
||||
"sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6",
|
||||
"sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d",
|
||||
"sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b",
|
||||
"sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e",
|
||||
"sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be",
|
||||
"sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c",
|
||||
"sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683",
|
||||
"sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9",
|
||||
"sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c",
|
||||
"sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8",
|
||||
"sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1",
|
||||
"sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4",
|
||||
"sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655",
|
||||
"sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67",
|
||||
"sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595",
|
||||
"sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0",
|
||||
"sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65",
|
||||
"sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41",
|
||||
"sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6",
|
||||
"sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401",
|
||||
"sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6",
|
||||
"sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3",
|
||||
"sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16",
|
||||
"sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93",
|
||||
"sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e",
|
||||
"sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4",
|
||||
"sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964",
|
||||
"sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c",
|
||||
"sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576",
|
||||
"sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0",
|
||||
"sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3",
|
||||
"sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662",
|
||||
"sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3",
|
||||
"sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff",
|
||||
"sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5",
|
||||
"sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd",
|
||||
"sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f",
|
||||
"sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5",
|
||||
"sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14",
|
||||
"sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d",
|
||||
"sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9",
|
||||
"sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7",
|
||||
"sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382",
|
||||
"sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a",
|
||||
"sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e",
|
||||
"sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a",
|
||||
"sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4",
|
||||
"sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99",
|
||||
"sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87",
|
||||
"sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"
|
||||
"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"
|
||||
],
|
||||
"markers": "platform_python_implementation != 'PyPy'",
|
||||
"version": "==1.17.1"
|
||||
"version": "==2.0.0"
|
||||
},
|
||||
"click": {
|
||||
"hashes": [
|
||||
"sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202",
|
||||
"sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"
|
||||
],
|
||||
"markers": "python_version >= '3.10'",
|
||||
"version": "==8.2.1"
|
||||
},
|
||||
"cryptography": {
|
||||
"hashes": [
|
||||
"sha256:04abd71114848aa25edb28e225ab5f268096f44cf0127f3d36975bdf1bdf3390",
|
||||
"sha256:0529b1d5a0105dd3731fa65680b45ce49da4d8115ea76e9da77a875396727b41",
|
||||
"sha256:1bc312dfb7a6e5d66082c87c34c8a62176e684b6fe3d90fcfe1568de675e6688",
|
||||
"sha256:268e4e9b177c76d569e8a145a6939eca9a5fec658c932348598818acf31ae9a5",
|
||||
"sha256:29ecec49f3ba3f3849362854b7253a9f59799e3763b0c9d0826259a88efa02f1",
|
||||
"sha256:2bf7bf75f7df9715f810d1b038870309342bff3069c5bd8c6b96128cb158668d",
|
||||
"sha256:3b721b8b4d948b218c88cb8c45a01793483821e709afe5f622861fc6182b20a7",
|
||||
"sha256:3c00b6b757b32ce0f62c574b78b939afab9eecaf597c4d624caca4f9e71e7843",
|
||||
"sha256:3dc62975e31617badc19a906481deacdeb80b4bb454394b4098e3f2525a488c5",
|
||||
"sha256:4973da6ca3db4405c54cd0b26d328be54c7747e89e284fcff166132eb7bccc9c",
|
||||
"sha256:4e389622b6927d8133f314949a9812972711a111d577a5d1f4bee5e58736b80a",
|
||||
"sha256:51e4de3af4ec3899d6d178a8c005226491c27c4ba84101bfb59c901e10ca9f79",
|
||||
"sha256:5f6f90b72d8ccadb9c6e311c775c8305381db88374c65fa1a68250aa8a9cb3a6",
|
||||
"sha256:6210c05941994290f3f7f175a4a57dbbb2afd9273657614c506d5976db061181",
|
||||
"sha256:6f101b1f780f7fc613d040ca4bdf835c6ef3b00e9bd7125a4255ec574c7916e4",
|
||||
"sha256:7bdcd82189759aba3816d1f729ce42ffded1ac304c151d0a8e89b9996ab863d5",
|
||||
"sha256:7ca25849404be2f8e4b3c59483d9d3c51298a22c1c61a0e84415104dacaf5562",
|
||||
"sha256:81276f0ea79a208d961c433a947029e1a15948966658cf6710bbabb60fcc2639",
|
||||
"sha256:8cadc6e3b5a1f144a039ea08a0bdb03a2a92e19c46be3285123d32029f40a922",
|
||||
"sha256:8e0ddd63e6bf1161800592c71ac794d3fb8001f2caebe0966e77c5234fa9efc3",
|
||||
"sha256:909c97ab43a9c0c0b0ada7a1281430e4e5ec0458e6d9244c0e821bbf152f061d",
|
||||
"sha256:96e7a5e9d6e71f9f4fca8eebfd603f8e86c5225bb18eb621b2c1e50b290a9471",
|
||||
"sha256:9a1e657c0f4ea2a23304ee3f964db058c9e9e635cc7019c4aa21c330755ef6fd",
|
||||
"sha256:9eb9d22b0a5d8fd9925a7764a054dca914000607dff201a24c791ff5c799e1fa",
|
||||
"sha256:af4ff3e388f2fa7bff9f7f2b31b87d5651c45731d3e8cfa0944be43dff5cfbdb",
|
||||
"sha256:b042d2a275c8cee83a4b7ae30c45a15e6a4baa65a179a0ec2d78ebb90e4f6699",
|
||||
"sha256:bc821e161ae88bfe8088d11bb39caf2916562e0a2dc7b6d56714a48b784ef0bb",
|
||||
"sha256:c505d61b6176aaf982c5717ce04e87da5abc9a36a5b39ac03905c4aafe8de7aa",
|
||||
"sha256:c63454aa261a0cf0c5b4718349629793e9e634993538db841165b3df74f37ec0",
|
||||
"sha256:c7362add18b416b69d58c910caa217f980c5ef39b23a38a0880dfd87bdf8cd23",
|
||||
"sha256:d03806036b4f89e3b13b6218fefea8d5312e450935b1a2d55f0524e2ed7c59d9",
|
||||
"sha256:d1b3031093a366ac767b3feb8bcddb596671b3aaff82d4050f984da0c248b615",
|
||||
"sha256:d1c3572526997b36f245a96a2b1713bf79ce99b271bbcf084beb6b9b075f29ea",
|
||||
"sha256:efcfe97d1b3c79e486554efddeb8f6f53a4cdd4cf6086642784fa31fc384e1d7",
|
||||
"sha256:f514ef4cd14bb6fb484b4a60203e912cfcb64f2ab139e88c2274511514bf7308"
|
||||
"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"
|
||||
],
|
||||
"markers": "python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1'",
|
||||
"version": "==44.0.2"
|
||||
},
|
||||
"ecdsa": {
|
||||
"hashes": [
|
||||
"sha256:30638e27cf77b7e15c4c4cc1973720149e1033827cfd00661ca5c8cc0cdb24c3",
|
||||
"sha256:478cba7b62555866fcb3bb3fe985e06decbdb68ef55713c4e5ab98c57d508e61"
|
||||
],
|
||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
|
||||
"version": "==0.19.1"
|
||||
"version": "==45.0.7"
|
||||
},
|
||||
"esptool": {
|
||||
"hashes": [
|
||||
"sha256:dc4ef26b659e1a8dcb019147c0ea6d94980b34de99fbe09121c7941c8b254531"
|
||||
"sha256:05cc4732eb2a9a7766c9e3531f7943d76ff0ca06dc9cd308d1d3d0b72f74aac2"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.8.1"
|
||||
"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",
|
||||
"sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==3.10"
|
||||
},
|
||||
"intelhex": {
|
||||
"hashes": [
|
||||
@@ -307,21 +343,54 @@
|
||||
],
|
||||
"version": "==2.3.0"
|
||||
},
|
||||
"markdown-it-py": {
|
||||
"hashes": [
|
||||
"sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147",
|
||||
"sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3"
|
||||
],
|
||||
"markers": "python_version >= '3.10'",
|
||||
"version": "==4.0.0"
|
||||
},
|
||||
"mdurl": {
|
||||
"hashes": [
|
||||
"sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8",
|
||||
"sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.1.2"
|
||||
},
|
||||
"mpremote": {
|
||||
"hashes": [
|
||||
"sha256:1a3c16d255748cfe54d4a897908651fc8286233173f7c7b2a0e56ae4b9fa940e",
|
||||
"sha256:d3ae3d0a0ae7713c537be2b6afadd11c7cde5f1750ea1260f6667bb80071b15b"
|
||||
"sha256:7f347318fb6d3bb8f89401d399a05efba39b51c74f747cebe92d3c6a9a4ee0b4",
|
||||
"sha256:daed9b795fdf98edb0c9c4f7f892bf66f075ec5e728bdcc4ab0915abf23d5d17"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.24.1"
|
||||
"markers": "python_version >= '3.4'",
|
||||
"version": "==1.26.0"
|
||||
},
|
||||
"platformdirs": {
|
||||
"hashes": [
|
||||
"sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85",
|
||||
"sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf"
|
||||
],
|
||||
"markers": "python_version >= '3.9'",
|
||||
"version": "==4.4.0"
|
||||
},
|
||||
"pycparser": {
|
||||
"hashes": [
|
||||
"sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6",
|
||||
"sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"
|
||||
"sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2",
|
||||
"sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"
|
||||
],
|
||||
"markers": "implementation_name != 'PyPy'",
|
||||
"version": "==2.23"
|
||||
},
|
||||
"pygments": {
|
||||
"hashes": [
|
||||
"sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887",
|
||||
"sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==2.22"
|
||||
"version": "==2.19.2"
|
||||
},
|
||||
"pyserial": {
|
||||
"hashes": [
|
||||
@@ -397,13 +466,159 @@
|
||||
],
|
||||
"version": "==1.7.0"
|
||||
},
|
||||
"six": {
|
||||
"rich": {
|
||||
"hashes": [
|
||||
"sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274",
|
||||
"sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"
|
||||
"sha256:536f5f1785986d6dbdea3c75205c473f970777b4a0d6c6dd1b696aa05a3fa04f",
|
||||
"sha256:e497a48b844b0320d45007cdebfeaeed8db2a4f4bcf49f15e455cfc4af11eaa8"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.17.0"
|
||||
"markers": "python_full_version >= '3.8.0'",
|
||||
"version": "==14.1.0"
|
||||
},
|
||||
"rich-click": {
|
||||
"hashes": [
|
||||
"sha256:c3fa81ed8a671a10de65a9e20abf642cfdac6fdb882db1ef465ee33919fbcfe2",
|
||||
"sha256:fd98c0ab9ddc1cf9c0b7463f68daf28b4d0033a74214ceb02f761b3ff2af3136"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==1.8.9"
|
||||
},
|
||||
"sniffio": {
|
||||
"hashes": [
|
||||
"sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2",
|
||||
"sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==1.3.1"
|
||||
},
|
||||
"typing-extensions": {
|
||||
"hashes": [
|
||||
"sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466",
|
||||
"sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"
|
||||
],
|
||||
"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"
|
||||
},
|
||||
"watchfiles": {
|
||||
"hashes": [
|
||||
"sha256:00645eb79a3faa70d9cb15c8d4187bb72970b2470e938670240c7998dad9f13a",
|
||||
"sha256:04e4ed5d1cd3eae68c89bcc1a485a109f39f2fd8de05f705e98af6b5f1861f1f",
|
||||
"sha256:0a7d40b77f07be87c6faa93d0951a0fcd8cbca1ddff60a1b65d741bac6f3a9f6",
|
||||
"sha256:0ece16b563b17ab26eaa2d52230c9a7ae46cf01759621f4fbbca280e438267b3",
|
||||
"sha256:11ee4444250fcbeb47459a877e5e80ed994ce8e8d20283857fc128be1715dac7",
|
||||
"sha256:12b0a02a91762c08f7264e2e79542f76870c3040bbc847fb67410ab81474932a",
|
||||
"sha256:12fe8eaffaf0faa7906895b4f8bb88264035b3f0243275e0bf24af0436b27259",
|
||||
"sha256:130fc497b8ee68dce163e4254d9b0356411d1490e868bd8790028bc46c5cc297",
|
||||
"sha256:17ab167cca6339c2b830b744eaf10803d2a5b6683be4d79d8475d88b4a8a4be1",
|
||||
"sha256:199207b2d3eeaeb80ef4411875a6243d9ad8bc35b07fc42daa6b801cc39cc41c",
|
||||
"sha256:20ecc8abbd957046f1fe9562757903f5eaf57c3bce70929fda6c7711bb58074a",
|
||||
"sha256:239736577e848678e13b201bba14e89718f5c2133dfd6b1f7846fa1b58a8532b",
|
||||
"sha256:249590eb75ccc117f488e2fabd1bfa33c580e24b96f00658ad88e38844a040bb",
|
||||
"sha256:27f30e14aa1c1e91cb653f03a63445739919aef84c8d2517997a83155e7a2fcc",
|
||||
"sha256:29e7bc2eee15cbb339c68445959108803dc14ee0c7b4eea556400131a8de462b",
|
||||
"sha256:328dbc9bff7205c215a7807da7c18dce37da7da718e798356212d22696404339",
|
||||
"sha256:32d6d4e583593cb8576e129879ea0991660b935177c0f93c6681359b3654bfa9",
|
||||
"sha256:3366f56c272232860ab45c77c3ca7b74ee819c8e1f6f35a7125556b198bbc6df",
|
||||
"sha256:3434e401f3ce0ed6b42569128b3d1e3af773d7ec18751b918b89cd49c14eaafb",
|
||||
"sha256:37d3d3f7defb13f62ece99e9be912afe9dd8a0077b7c45ee5a57c74811d581a4",
|
||||
"sha256:3a6fd40bbb50d24976eb275ccb55cd1951dfb63dbc27cae3066a6ca5f4beabd5",
|
||||
"sha256:3aba215958d88182e8d2acba0fdaf687745180974946609119953c0e112397dc",
|
||||
"sha256:406520216186b99374cdb58bc48e34bb74535adec160c8459894884c983a149c",
|
||||
"sha256:4281cd9fce9fc0a9dbf0fc1217f39bf9cf2b4d315d9626ef1d4e87b84699e7e8",
|
||||
"sha256:42f92befc848bb7a19658f21f3e7bae80d7d005d13891c62c2cd4d4d0abb3433",
|
||||
"sha256:48aa25e5992b61debc908a61ab4d3f216b64f44fdaa71eb082d8b2de846b7d12",
|
||||
"sha256:5007f860c7f1f8df471e4e04aaa8c43673429047d63205d1630880f7637bca30",
|
||||
"sha256:50a51a90610d0845a5931a780d8e51d7bd7f309ebc25132ba975aca016b576a0",
|
||||
"sha256:51556d5004887045dba3acdd1fdf61dddea2be0a7e18048b5e853dcd37149b86",
|
||||
"sha256:51b81e55d40c4b4aa8658427a3ee7ea847c591ae9e8b81ef94a90b668999353c",
|
||||
"sha256:5366164391873ed76bfdf618818c82084c9db7fac82b64a20c44d335eec9ced5",
|
||||
"sha256:54062ef956807ba806559b3c3d52105ae1827a0d4ab47b621b31132b6b7e2866",
|
||||
"sha256:60022527e71d1d1fda67a33150ee42869042bce3d0fcc9cc49be009a9cded3fb",
|
||||
"sha256:622d6b2c06be19f6e89b1d951485a232e3b59618def88dbeda575ed8f0d8dbf2",
|
||||
"sha256:62cc7a30eeb0e20ecc5f4bd113cd69dcdb745a07c68c0370cea919f373f65d9e",
|
||||
"sha256:693ed7ec72cbfcee399e92c895362b6e66d63dac6b91e2c11ae03d10d503e575",
|
||||
"sha256:6d2404af8db1329f9a3c9b79ff63e0ae7131986446901582067d9304ae8aaf7f",
|
||||
"sha256:7049e52167fc75fc3cc418fc13d39a8e520cbb60ca08b47f6cedb85e181d2f2a",
|
||||
"sha256:7080c4bb3efd70a07b1cc2df99a7aa51d98685be56be6038c3169199d0a1c69f",
|
||||
"sha256:7738027989881e70e3723c75921f1efa45225084228788fc59ea8c6d732eb30d",
|
||||
"sha256:7a7bd57a1bb02f9d5c398c0c1675384e7ab1dd39da0ca50b7f09af45fa435277",
|
||||
"sha256:7b3443f4ec3ba5aa00b0e9fa90cf31d98321cbff8b925a7c7b84161619870bc9",
|
||||
"sha256:7c55b0f9f68590115c25272b06e63f0824f03d4fc7d6deed43d8ad5660cabdbf",
|
||||
"sha256:7fd1b3879a578a8ec2076c7961076df540b9af317123f84569f5a9ddee64ce92",
|
||||
"sha256:8076a5769d6bdf5f673a19d51da05fc79e2bbf25e9fe755c47595785c06a8c72",
|
||||
"sha256:80f811146831c8c86ab17b640801c25dc0a88c630e855e2bef3568f30434d52b",
|
||||
"sha256:8412eacef34cae2836d891836a7fff7b754d6bcac61f6c12ba5ca9bc7e427b68",
|
||||
"sha256:865c8e95713744cf5ae261f3067861e9da5f1370ba91fc536431e29b418676fa",
|
||||
"sha256:86b1e28d4c37e89220e924305cd9f82866bb0ace666943a6e4196c5df4d58dcc",
|
||||
"sha256:891c69e027748b4a73847335d208e374ce54ca3c335907d381fde4e41661b13b",
|
||||
"sha256:8ac164e20d17cc285f2b94dc31c384bc3aa3dd5e7490473b3db043dd70fbccfd",
|
||||
"sha256:8c5701dc474b041e2934a26d31d39f90fac8a3dee2322b39f7729867f932b1d4",
|
||||
"sha256:90ebb429e933645f3da534c89b29b665e285048973b4d2b6946526888c3eb2c7",
|
||||
"sha256:923fec6e5461c42bd7e3fd5ec37492c6f3468be0499bc0707b4bbbc16ac21792",
|
||||
"sha256:935f9edd022ec13e447e5723a7d14456c8af254544cefbc533f6dd276c9aa0d9",
|
||||
"sha256:95ab1594377effac17110e1352989bdd7bdfca9ff0e5eeccd8c69c5389b826d0",
|
||||
"sha256:9974d2f7dc561cce3bb88dfa8eb309dab64c729de85fba32e98d75cf24b66297",
|
||||
"sha256:9c733cda03b6d636b4219625a4acb5c6ffb10803338e437fb614fef9516825ef",
|
||||
"sha256:9dc001c3e10de4725c749d4c2f2bdc6ae24de5a88a339c4bce32300a31ede179",
|
||||
"sha256:9f811079d2f9795b5d48b55a37aa7773680a5659afe34b54cc1d86590a51507d",
|
||||
"sha256:a2726d7bfd9f76158c84c10a409b77a320426540df8c35be172444394b17f7ea",
|
||||
"sha256:a479466da6db5c1e8754caee6c262cd373e6e6c363172d74394f4bff3d84d7b5",
|
||||
"sha256:a543492513a93b001975ae283a51f4b67973662a375a403ae82f420d2c7205ee",
|
||||
"sha256:a89c75a5b9bc329131115a409d0acc16e8da8dfd5867ba59f1dd66ae7ea8fa82",
|
||||
"sha256:a8f6f72974a19efead54195bc9bed4d850fc047bb7aa971268fd9a8387c89011",
|
||||
"sha256:a9ccbf1f129480ed3044f540c0fdbc4ee556f7175e5ab40fe077ff6baf286d4e",
|
||||
"sha256:aa0cc8365ab29487eb4f9979fd41b22549853389e22d5de3f134a6796e1b05a4",
|
||||
"sha256:adb4167043d3a78280d5d05ce0ba22055c266cf8655ce942f2fb881262ff3cdf",
|
||||
"sha256:af06c863f152005c7592df1d6a7009c836a247c9d8adb78fef8575a5a98699db",
|
||||
"sha256:b067915e3c3936966a8607f6fe5487df0c9c4afb85226613b520890049deea20",
|
||||
"sha256:b7c5f6fe273291f4d414d55b2c80d33c457b8a42677ad14b4b47ff025d0893e4",
|
||||
"sha256:b915daeb2d8c1f5cee4b970f2e2c988ce6514aace3c9296e58dd64dc9aa5d575",
|
||||
"sha256:ba0e3255b0396cac3cc7bbace76404dd72b5438bf0d8e7cefa2f79a7f3649caa",
|
||||
"sha256:bda8136e6a80bdea23e5e74e09df0362744d24ffb8cd59c4a95a6ce3d142f79c",
|
||||
"sha256:bfe3c517c283e484843cb2e357dd57ba009cff351edf45fb455b5fbd1f45b15f",
|
||||
"sha256:c588c45da9b08ab3da81d08d7987dae6d2a3badd63acdb3e206a42dbfa7cb76f",
|
||||
"sha256:c600e85f2ffd9f1035222b1a312aff85fd11ea39baff1d705b9b047aad2ce267",
|
||||
"sha256:c68e9f1fcb4d43798ad8814c4c1b61547b014b667216cb754e606bfade587018",
|
||||
"sha256:c9649dfc57cc1f9835551deb17689e8d44666315f2e82d337b9f07bd76ae3aa2",
|
||||
"sha256:cb45350fd1dc75cd68d3d72c47f5b513cb0578da716df5fba02fff31c69d5f2d",
|
||||
"sha256:cbcf8630ef4afb05dc30107bfa17f16c0896bb30ee48fc24bf64c1f970f3b1fd",
|
||||
"sha256:cbd949bdd87567b0ad183d7676feb98136cde5bb9025403794a4c0db28ed3a47",
|
||||
"sha256:cc08ef8b90d78bfac66f0def80240b0197008e4852c9f285907377b2947ffdcb",
|
||||
"sha256:cd17a1e489f02ce9117b0de3c0b1fab1c3e2eedc82311b299ee6b6faf6c23a29",
|
||||
"sha256:d05686b5487cfa2e2c28ff1aa370ea3e6c5accfe6435944ddea1e10d93872147",
|
||||
"sha256:d0e10e6f8f6dc5762adee7dece33b722282e1f59aa6a55da5d493a97282fedd8",
|
||||
"sha256:d181ef50923c29cf0450c3cd47e2f0557b62218c50b2ab8ce2ecaa02bd97e670",
|
||||
"sha256:d1caf40c1c657b27858f9774d5c0e232089bca9cb8ee17ce7478c6e9264d2587",
|
||||
"sha256:d7642b9bc4827b5518ebdb3b82698ada8c14c7661ddec5fe719f3e56ccd13c97",
|
||||
"sha256:d9481174d3ed982e269c090f780122fb59cee6c3796f74efe74e70f7780ed94c",
|
||||
"sha256:d9ba68ec283153dead62cbe81872d28e053745f12335d037de9cbd14bd1877f5",
|
||||
"sha256:da71945c9ace018d8634822f16cbc2a78323ef6c876b1d34bbf5d5222fd6a72e",
|
||||
"sha256:dc44678a72ac0910bac46fa6a0de6af9ba1355669b3dfaf1ce5f05ca7a74364e",
|
||||
"sha256:df32d59cb9780f66d165a9a7a26f19df2c7d24e3bd58713108b41d0ff4f929c6",
|
||||
"sha256:df670918eb7dd719642e05979fc84704af913d563fd17ed636f7c4783003fdcc",
|
||||
"sha256:e78b6ed8165996013165eeabd875c5dfc19d41b54f94b40e9fff0eb3193e5e8e",
|
||||
"sha256:ed8fc66786de8d0376f9f913c09e963c66e90ced9aa11997f93bdb30f7c872a8",
|
||||
"sha256:eff4b8d89f444f7e49136dc695599a591ff769300734446c0a86cba2eb2f9895",
|
||||
"sha256:f21af781a4a6fbad54f03c598ab620e3a77032c5878f3d780448421a6e1818c7",
|
||||
"sha256:f2bcdc54ea267fe72bfc7d83c041e4eb58d7d8dc6f578dfddb52f037ce62f432",
|
||||
"sha256:f2f0498b7d2a3c072766dba3274fe22a183dbea1f99d188f1c6c72209a1063dc",
|
||||
"sha256:f7208ab6e009c627b7557ce55c465c98967e8caa8b11833531fdf95799372633",
|
||||
"sha256:f7590d5a455321e53857892ab8879dce62d1f4b04748769f5adf2e707afb9d4f",
|
||||
"sha256:fa257a4d0d21fcbca5b5fcba9dca5a78011cb93c0323fb8855c6d2dfbc76eb77",
|
||||
"sha256:fba9b62da882c1be1280a7584ec4515d0a6006a94d6e5819730ec2eab60ffe12",
|
||||
"sha256:fe4371595edf78c41ef8ac8df20df3943e13defd0efcb732b2e393b5a8a7a71f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_version >= '3.9'",
|
||||
"version": "==1.1.0"
|
||||
}
|
||||
},
|
||||
"develop": {}
|
||||
|
58
configure_bar.py
Normal file
58
configure_bar.py
Normal file
@@ -0,0 +1,58 @@
|
||||
#!/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()
|
428
patterns.py
Normal file
428
patterns.py
Normal file
@@ -0,0 +1,428 @@
|
||||
|
||||
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)
|
||||
|
||||
for i in range(self.num_leds):
|
||||
rc_index = (i * 256 // self.num_leds) + self.pattern_step
|
||||
self.n[i] = self.apply_brightness(wheel(rc_index & 255))
|
||||
self.n.write()
|
||||
self.pattern_step = (self.pattern_step + 1) % 256
|
||||
self.last_update = current_time
|
||||
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 ---")
|
||||
|
||||
|
@@ -6,4 +6,4 @@ s = Settings()
|
||||
|
||||
name = s.get('name', 'led')
|
||||
password = s.get("ap_password", "")
|
||||
wifi.ap(name, password)
|
||||
# wifi.ap(name, password)
|
||||
|
99
src/main.py
99
src/main.py
@@ -1,5 +1,5 @@
|
||||
import asyncio
|
||||
import aioespnow
|
||||
|
||||
import patterns
|
||||
from settings import Settings
|
||||
from web import web
|
||||
from patterns import Patterns
|
||||
@@ -10,43 +10,76 @@ import time
|
||||
import wifi
|
||||
import json
|
||||
from p2p import p2p
|
||||
import espnow
|
||||
import network
|
||||
|
||||
async def main():
|
||||
def main():
|
||||
settings = Settings()
|
||||
|
||||
patterns = Patterns(settings["led_pin"], settings["num_leds"], selected=settings["pattern"])
|
||||
if settings["color_order"] == "rbg": color_order = (1, 5, 3)
|
||||
else: color_order = (1, 3, 5)
|
||||
patterns.set_color1(tuple(int(settings["color1"][i:i+2], 16) for i in color_order))
|
||||
patterns.set_color2(tuple(int(settings["color2"][i:i+2], 16) for i in color_order))
|
||||
patterns.set_brightness(int(settings["brightness"]))
|
||||
patterns.set_delay(int(settings["delay"]))
|
||||
|
||||
async def tick():
|
||||
while True:
|
||||
patterns.tick()
|
||||
await asyncio.sleep_ms(1)
|
||||
|
||||
w = web(settings, patterns)
|
||||
print(settings)
|
||||
# start the server in a bacakground task
|
||||
print("Starting")
|
||||
server = asyncio.create_task(w.start_server(host="0.0.0.0", port=80))
|
||||
|
||||
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"], selected="off")
|
||||
|
||||
sta_if = network.WLAN(network.STA_IF)
|
||||
sta_if.active(True)
|
||||
|
||||
e = espnow.ESPNow()
|
||||
e.config(rxbuf=1024)
|
||||
e.active(True)
|
||||
# Increase buffer size for 8-bar payloads (default 526 bytes might be too small) # Set to 1KB to handle larger multi-bar payloads
|
||||
|
||||
wdt = machine.WDT(timeout=10000)
|
||||
wdt.feed()
|
||||
|
||||
asyncio.create_task(tick())
|
||||
asyncio.create_task(p2p(settings, patterns))
|
||||
|
||||
while True:
|
||||
|
||||
#print(time.localtime())
|
||||
gc.collect()
|
||||
for i in range(20):
|
||||
# advance pattern based on its own returned schedule
|
||||
# due = patterns.tick(due)
|
||||
wdt.feed()
|
||||
await asyncio.sleep_ms(1000)
|
||||
|
||||
# cleanup before ending the application
|
||||
await server
|
||||
# Drain all pending packets and only process the latest
|
||||
last_msg = None
|
||||
while True:
|
||||
host, msg = e.recv(0)
|
||||
if not msg:
|
||||
break
|
||||
last_msg = msg
|
||||
|
||||
asyncio.run(main())
|
||||
if last_msg:
|
||||
try:
|
||||
data = json.loads(last_msg)
|
||||
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
|
||||
patterns.brightness = bar.get("br", defaults.get("br", patterns.brightness))
|
||||
patterns.delay = bar.get("dl", defaults.get("dl", patterns.delay))
|
||||
patterns.colors = bar.get("cl", defaults.get("cl", patterns.colors))
|
||||
patterns.n1 = bar.get("n1", defaults.get("n1", patterns.n1))
|
||||
patterns.n2 = bar.get("n2", defaults.get("n2", patterns.n2))
|
||||
patterns.n3 = bar.get("n3", defaults.get("n3", patterns.n3))
|
||||
patterns.step = bar.get("s", defaults.get("s", patterns.step))
|
||||
|
||||
# Only execute pattern if it's a beat message
|
||||
if message_type == "b": # Beat message
|
||||
selected_pattern = bar.get("pt", defaults.get("pt", "off"))
|
||||
if selected_pattern in patterns.patterns:
|
||||
# Run the selected pattern ONCE in response to this beat message
|
||||
patterns.patterns[selected_pattern]()
|
||||
else:
|
||||
print(f"Pattern {selected_pattern} not found")
|
||||
elif message_type == "u": # Update message
|
||||
# Just update parameters, don't execute pattern
|
||||
print(f"Parameters updated: brightness={patterns.brightness}, delay={patterns.delay}")
|
||||
else:
|
||||
print(f"Unknown message type: {message_type}")
|
||||
except Exception as ex:
|
||||
print(f"Failed to load espnow data {last_msg}: {ex}")
|
||||
continue
|
||||
|
||||
|
||||
main()
|
||||
|
679
src/patterns.py
679
src/patterns.py
@@ -1,379 +1,344 @@
|
||||
from machine import Pin
|
||||
from neopixel import NeoPixel
|
||||
|
||||
import utime
|
||||
import random
|
||||
from patterns_base import PatternBase # Import PatternBase
|
||||
|
||||
class Patterns:
|
||||
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):
|
||||
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
|
||||
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.n3 = 1 # Default step factor
|
||||
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_step,
|
||||
"rainbow_cycle": self.rainbow_cycle_step,
|
||||
"theater_chase": self.theater_chase_step,
|
||||
"blink": self.blink_step,
|
||||
"random_color_wipe": self.random_color_wipe_step,
|
||||
"random_rainbow_cycle": self.random_rainbow_cycle_step,
|
||||
"random_theater_chase": self.random_theater_chase_step,
|
||||
"random_blink": self.random_blink_step,
|
||||
"color_transition": self.color_transition_step, # Added new pattern
|
||||
"flicker": self.flicker_step,
|
||||
"external": None
|
||||
"off": self.off,
|
||||
"flicker": self.flicker,
|
||||
"fill_range": self.fill_range,
|
||||
"n_chase": self.n_chase,
|
||||
"alternating": self.alternating,
|
||||
"pulse": self.pulse,
|
||||
"rainbow": self.rainbow,
|
||||
"specto": self.specto,
|
||||
"radiate": self.radiate,
|
||||
# Shortened pattern names for optimized JSON payloads
|
||||
"o": self.off,
|
||||
"f": self.flicker,
|
||||
"fr": self.fill_range,
|
||||
"nc": self.n_chase,
|
||||
"a": self.alternating,
|
||||
"p": self.pulse,
|
||||
"r": self.rainbow,
|
||||
"s": self.specto,
|
||||
"rd": self.radiate,
|
||||
}
|
||||
self.selected = selected
|
||||
# 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
|
||||
|
||||
|
||||
def sync(self):
|
||||
self.pattern_step=0
|
||||
self.last_update = utime.ticks_ms() - self.delay
|
||||
if self.selected == "color_transition":
|
||||
self.transition_step = 0
|
||||
self.current_color_idx = 0
|
||||
self.current_color = self.colors[self.current_color_idx]
|
||||
self.hold_start_time = utime.ticks_ms() # Reset hold time
|
||||
self.tick()
|
||||
|
||||
def set_pattern_step(self, step):
|
||||
self.pattern_step = step
|
||||
|
||||
def tick(self):
|
||||
if self.patterns[self.selected]:
|
||||
self.patterns[self.selected]()
|
||||
|
||||
def update_num_leds(self, pin, num_leds):
|
||||
self.n = NeoPixel(Pin(pin, Pin.OUT), num_leds)
|
||||
self.num_leds = num_leds
|
||||
self.pattern_step = 0
|
||||
|
||||
def set_delay(self, delay):
|
||||
self.delay = delay
|
||||
# Update transition duration and hold duration when delay changes
|
||||
self.transition_duration = self.delay * 50
|
||||
self.hold_duration = self.delay * 10
|
||||
|
||||
|
||||
def set_brightness(self, brightness):
|
||||
self.brightness = brightness
|
||||
|
||||
def set_color1(self, color):
|
||||
if len(self.colors) > 0:
|
||||
self.colors[0] = color
|
||||
if self.selected == "color_transition":
|
||||
# If the first color is changed, potentially reset transition
|
||||
# to start from this new color if we were about to transition from it
|
||||
if self.current_color_idx == 0:
|
||||
self.transition_step = 0
|
||||
self.current_color = self.colors[0]
|
||||
self.hold_start_time = utime.ticks_ms()
|
||||
else:
|
||||
self.colors.append(color)
|
||||
|
||||
|
||||
def set_color2(self, color):
|
||||
if len(self.colors) > 1:
|
||||
self.colors[1] = color
|
||||
elif len(self.colors) == 1:
|
||||
self.colors.append(color)
|
||||
else: # List is empty
|
||||
self.colors.append((0,0,0)) # Dummy color
|
||||
self.colors.append(color)
|
||||
|
||||
|
||||
def set_colors(self, colors):
|
||||
if colors and len(colors) >= 2:
|
||||
self.colors = colors
|
||||
if self.selected == "color_transition":
|
||||
self.sync() # Reset transition if new color list is provided
|
||||
elif colors and len(colors) == 1:
|
||||
self.colors = [colors[0], (255,255,255)] # Add a default second color
|
||||
if self.selected == "color_transition":
|
||||
print("Warning: 'color_transition' requires at least two colors. Adding a default second color.")
|
||||
self.sync()
|
||||
else:
|
||||
print("Error: set_colors requires a list of at least one color.")
|
||||
self.colors = [(0,0,0), (255,255,255)] # Fallback
|
||||
if self.selected == "color_transition":
|
||||
self.sync()
|
||||
|
||||
def set_color(self, num, color):
|
||||
# Changed: More robust index check
|
||||
if 0 <= num < len(self.colors):
|
||||
self.colors[num] = color
|
||||
# If the changed color is part of the current or next transition,
|
||||
# restart the transition for smoother updates
|
||||
if self.selected == "color_transition":
|
||||
current_from_idx = self.current_color_idx
|
||||
current_to_idx = (self.current_color_idx + 1) % len(self.colors)
|
||||
if num == current_from_idx or num == current_to_idx:
|
||||
# If we change a color involved in the current transition,
|
||||
# it's best to restart the transition state for smoothness.
|
||||
self.transition_step = 0
|
||||
self.current_color_idx = current_from_idx # Stay at the current starting color
|
||||
self.current_color = self.colors[self.current_color_idx]
|
||||
self.hold_start_time = utime.ticks_ms() # Reset hold
|
||||
return True
|
||||
elif num == len(self.colors): # Allow setting a new color at the end
|
||||
self.colors.append(color)
|
||||
return True
|
||||
return False
|
||||
|
||||
def add_color(self, color):
|
||||
self.colors.append(color)
|
||||
if self.selected == "color_transition" and len(self.colors) == 2:
|
||||
# If we just added the second color needed for transition
|
||||
self.sync()
|
||||
|
||||
|
||||
def del_color(self, num):
|
||||
# Changed: More robust index check and using del for lists
|
||||
if 0 <= num < len(self.colors):
|
||||
del self.colors[num]
|
||||
# If the color being deleted was part of the current transition,
|
||||
# re-evaluate the current_color_idx
|
||||
if self.selected == "color_transition":
|
||||
if len(self.colors) < 2: # Need at least two colors for transition
|
||||
print("Warning: Not enough colors for 'color_transition'. Switching to 'on'.")
|
||||
self.select("on") # Or some other default
|
||||
else:
|
||||
# Adjust index if it's out of bounds after deletion or was the one transitioning from
|
||||
self.current_color_idx %= len(self.colors)
|
||||
self.transition_step = 0
|
||||
self.current_color = self.colors[self.current_color_idx]
|
||||
self.hold_start_time = utime.ticks_ms()
|
||||
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 select(self, pattern):
|
||||
if pattern in self.patterns:
|
||||
self.selected = pattern
|
||||
self.sync() # Reset pattern state when selecting a new pattern
|
||||
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 set(self, i, color):
|
||||
self.n[i] = color
|
||||
|
||||
def write(self):
|
||||
self.n.write()
|
||||
|
||||
def fill(self, color=None):
|
||||
fill_color = color if color is not None else self.colors[0]
|
||||
for i in range(self.num_leds):
|
||||
self.n[i] = fill_color
|
||||
self.n.write()
|
||||
|
||||
def off(self):
|
||||
self.fill((0, 0, 0))
|
||||
self.step = 0
|
||||
|
||||
def on(self):
|
||||
"""Turn on all LEDs with current color"""
|
||||
self.fill(self.apply_brightness(self.colors[0]))
|
||||
|
||||
def color_wipe_step(self):
|
||||
color = self.apply_brightness(self.colors[0])
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay:
|
||||
if self.pattern_step < self.num_leds:
|
||||
for i in range(self.num_leds):
|
||||
self.n[i] = (0, 0, 0)
|
||||
self.n[self.pattern_step] = self.apply_brightness(color)
|
||||
self.n.write()
|
||||
self.pattern_step += 1
|
||||
else:
|
||||
self.pattern_step = 0
|
||||
self.last_update = current_time
|
||||
return self.delay
|
||||
|
||||
def rainbow_cycle_step(self):
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay/5:
|
||||
def wheel(pos):
|
||||
if pos < 85:
|
||||
return (pos * 3, 255 - pos * 3, 0)
|
||||
elif pos < 170:
|
||||
pos -= 85
|
||||
return (255 - pos * 3, 0, pos * 3)
|
||||
else:
|
||||
pos -= 170
|
||||
return (0, pos * 3, 255 - pos * 3)
|
||||
|
||||
for i in range(self.num_leds):
|
||||
rc_index = (i * 256 // self.num_leds) + self.pattern_step
|
||||
self.n[i] = self.apply_brightness(wheel(rc_index & 255))
|
||||
self.n.write()
|
||||
self.pattern_step = (self.pattern_step + 1) % 256
|
||||
self.last_update = current_time
|
||||
|
||||
def theater_chase_step(self):
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay:
|
||||
for i in range(self.num_leds):
|
||||
if (i + self.pattern_step) % 3 == 0:
|
||||
self.n[i] = self.apply_brightness(self.colors[0])
|
||||
else:
|
||||
self.n[i] = (0, 0, 0)
|
||||
self.n.write()
|
||||
self.pattern_step = (self.pattern_step + 1) % 3
|
||||
self.last_update = current_time
|
||||
|
||||
def blink_step(self):
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay:
|
||||
if self.pattern_step % 2 == 0:
|
||||
self.fill(self.apply_brightness(self.colors[0]))
|
||||
else:
|
||||
def off(self):
|
||||
"""Turn off all LEDs"""
|
||||
self.fill((0, 0, 0))
|
||||
self.pattern_step = (self.pattern_step + 1) % 2
|
||||
self.last_update = current_time
|
||||
|
||||
def random_color_wipe_step(self):
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay:
|
||||
color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
|
||||
if self.pattern_step < self.num_leds:
|
||||
for i in range(self.num_leds):
|
||||
self.n[i] = (0, 0, 0)
|
||||
self.n[self.pattern_step] = self.apply_brightness(color)
|
||||
self.n.write()
|
||||
self.pattern_step += 1
|
||||
else:
|
||||
self.pattern_step = 0
|
||||
self.last_update = current_time
|
||||
return self.delay
|
||||
|
||||
def random_rainbow_cycle_step(self):
|
||||
def flicker(self):
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay: # Kept original delay for now
|
||||
def wheel(pos):
|
||||
if pos < 85:
|
||||
return (pos * 3, 255 - pos * 3, 0)
|
||||
elif pos < 170:
|
||||
pos -= 85
|
||||
return (255 - pos * 3, 0, pos * 3)
|
||||
else:
|
||||
pos -= 170
|
||||
return (0, pos * 3, 255 - pos * 3)
|
||||
|
||||
random_offset = random.randint(0, 255)
|
||||
for i in range(self.num_leds):
|
||||
rc_index = (i * 256 // self.num_leds) + self.pattern_step + random_offset
|
||||
self.n[i] = self.apply_brightness(wheel(rc_index & 255))
|
||||
self.n.write()
|
||||
self.pattern_step = (self.pattern_step + 1) % 256
|
||||
self.last_update = current_time
|
||||
|
||||
def random_theater_chase_step(self):
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay:
|
||||
color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
|
||||
for i in range(self.num_leds):
|
||||
if (i + self.pattern_step) % 3 == 0:
|
||||
self.n[i] = self.apply_brightness(color)
|
||||
else:
|
||||
self.n[i] = (0, 0, 0)
|
||||
self.n.write()
|
||||
self.pattern_step = (self.pattern_step + 1) % 3
|
||||
self.last_update = current_time
|
||||
|
||||
def random_blink_step(self):
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay*10:
|
||||
color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
|
||||
if self.pattern_step % 2 == 0:
|
||||
self.fill(self.apply_brightness(color))
|
||||
else:
|
||||
self.fill((0, 0, 0))
|
||||
self.pattern_step = (self.pattern_step + 1) % 2
|
||||
self.last_update = current_time
|
||||
|
||||
def color_transition_step(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
|
||||
|
||||
# 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
|
||||
|
||||
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
|
||||
|
||||
def flicker_step(self):
|
||||
current_time = utime.ticks_ms()
|
||||
if utime.ticks_diff(current_time, self.last_update) >= self.delay/5:
|
||||
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)
|
||||
# Use fixed minimum brightness of 10, flicker between 10 and full brightness
|
||||
# Use n3 as step rate multiplier to control how fast patterns step
|
||||
min_brightness = 10
|
||||
step_rate = max(1, int(self.n3))
|
||||
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_brightness = max(min_brightness, 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 * step_rate)))
|
||||
|
||||
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()
|
||||
step_rate = max(1, int(self.n3))
|
||||
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
|
||||
|
||||
# Use controller's step for synchronization, but scale it for chasing
|
||||
chase_step = (self.step * step_rate) % self.num_leds
|
||||
|
||||
for i in range(self.num_leds):
|
||||
# Calculate position relative to the chase head
|
||||
pos_from_head = (i - chase_step) % self.num_leds
|
||||
if pos_from_head < self.n1:
|
||||
self.n[i] = self.apply_brightness(self.colors[0])
|
||||
else:
|
||||
self.n[i] = (0, 0, 0)
|
||||
self.n.write()
|
||||
|
||||
# Don't update internal step - use controller's step for sync
|
||||
self.last_update = current_time
|
||||
return self.delay
|
||||
|
||||
def alternating(self):
|
||||
# Use n1 as ON width and n2 as OFF width
|
||||
segment_on = max(0, int(self.n1))
|
||||
segment_off = max(0, int(self.n2))
|
||||
total_segment_length = segment_on + segment_off
|
||||
if total_segment_length <= 0:
|
||||
self.fill((0, 0, 0))
|
||||
self.n.write()
|
||||
return self.delay
|
||||
|
||||
current_phase = self.step % 2
|
||||
|
||||
active_color = self.apply_brightness(self.colors[0])
|
||||
|
||||
for i in range(self.num_leds):
|
||||
pos_in_segment = i % total_segment_length
|
||||
if current_phase == 0:
|
||||
# ON then OFF
|
||||
if pos_in_segment < segment_on:
|
||||
self.n[i] = active_color
|
||||
else:
|
||||
self.n[i] = (0, 0, 0)
|
||||
else:
|
||||
# OFF then ON
|
||||
if pos_in_segment < segment_on:
|
||||
self.n[i] = (0, 0, 0)
|
||||
else:
|
||||
self.n[i] = active_color
|
||||
|
||||
self.n.write()
|
||||
# Don't update step - use the step value sent from controller for synchronization
|
||||
return max(1, int(self.delay // 2))
|
||||
|
||||
|
||||
def pulse(self):
|
||||
# Envelope: attack=n1 ms, hold=delay ms, decay=n2 ms
|
||||
attack_ms = max(0, int(self.n1))
|
||||
hold_ms = max(0, int(self.delay))
|
||||
decay_ms = max(0, int(self.n2))
|
||||
|
||||
base = self.colors[0] if len(self.colors) > 0 else (255, 255, 255)
|
||||
full_brightness = max(0, min(255, int(self.brightness)))
|
||||
|
||||
# Attack phase (0 -> full)
|
||||
if attack_ms > 0:
|
||||
start = utime.ticks_ms()
|
||||
while utime.ticks_diff(utime.ticks_ms(), start) < attack_ms:
|
||||
elapsed = utime.ticks_diff(utime.ticks_ms(), start)
|
||||
frac = elapsed / attack_ms if attack_ms > 0 else 1.0
|
||||
b = int(full_brightness * frac)
|
||||
self.fill(self.apply_brightness(base, brightness_override=b))
|
||||
else:
|
||||
self.fill(self.apply_brightness(base, brightness_override=full_brightness))
|
||||
|
||||
# Hold phase
|
||||
if hold_ms > 0:
|
||||
start = utime.ticks_ms()
|
||||
while utime.ticks_diff(utime.ticks_ms(), start) < hold_ms:
|
||||
pass
|
||||
|
||||
# Decay phase (full -> 0)
|
||||
if decay_ms > 0:
|
||||
start = utime.ticks_ms()
|
||||
while utime.ticks_diff(utime.ticks_ms(), start) < decay_ms:
|
||||
elapsed = utime.ticks_diff(utime.ticks_ms(), start)
|
||||
frac = 1.0 - (elapsed / decay_ms if decay_ms > 0 else 1.0)
|
||||
if frac < 0:
|
||||
frac = 0
|
||||
b = int(full_brightness * frac)
|
||||
self.fill(self.apply_brightness(base, brightness_override=b))
|
||||
|
||||
# Ensure off at the end and stop auto-run
|
||||
self.fill((0, 0, 0))
|
||||
self.run = False
|
||||
return self.delay
|
||||
|
||||
def rainbow(self):
|
||||
# Wheel function to map 0-255 to RGB
|
||||
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)
|
||||
|
||||
step_rate = max(1, int(self.n3))
|
||||
# Use controller's step for synchronization, scaled for rainbow cycling
|
||||
rainbow_step = (self.step * step_rate) % 256
|
||||
|
||||
for i in range(self.num_leds):
|
||||
rc_index = (i * 256 // max(1, self.num_leds)) + rainbow_step
|
||||
self.n[i] = self.apply_brightness(wheel(rc_index & 255))
|
||||
self.n.write()
|
||||
|
||||
# Don't update internal step - use controller's step for sync
|
||||
return max(1, int(self.delay // 5))
|
||||
|
||||
def specto(self):
|
||||
# Light up LEDs from 0 up to n1 (exclusive) and turn the rest off
|
||||
count = int(self.n1)
|
||||
if count < 0:
|
||||
count = 0
|
||||
if count > self.num_leds:
|
||||
count = self.num_leds
|
||||
color = self.apply_brightness(self.colors[0] if len(self.colors) > 0 else (255, 255, 255))
|
||||
for i in range(self.num_leds):
|
||||
self.n[i] = color if i < count else (0, 0, 0)
|
||||
self.n.write()
|
||||
return self.delay
|
||||
|
||||
def radiate(self):
|
||||
# Radiate outward from origins spaced every n1 LEDs, stepping each ring by self.delay
|
||||
sep = max(1, int(self.n1) if self.n1 else 1)
|
||||
color = self.apply_brightness(self.colors[0] if len(self.colors) > 0 else (255, 255, 255))
|
||||
|
||||
# Start with strip off
|
||||
self.fill((0, 0, 0))
|
||||
|
||||
origins = list(range(0, self.num_leds, sep))
|
||||
radius = 0
|
||||
lit_total = 0
|
||||
while True:
|
||||
drew_any = False
|
||||
for o in origins:
|
||||
left = o - radius
|
||||
right = o + radius
|
||||
if 0 <= left < self.num_leds:
|
||||
if self.n[left] == (0, 0, 0):
|
||||
lit_total += 1
|
||||
self.n[left] = color
|
||||
drew_any = True
|
||||
if 0 <= right < self.num_leds:
|
||||
if self.n[right] == (0, 0, 0):
|
||||
lit_total += 1
|
||||
self.n[right] = color
|
||||
drew_any = True
|
||||
self.n.write()
|
||||
|
||||
# If we didn't draw anything new, we've reached beyond edges
|
||||
if not drew_any:
|
||||
break
|
||||
# If all LEDs are now lit, immediately proceed to dark sweep
|
||||
if lit_total >= self.num_leds:
|
||||
break
|
||||
# wait self.delay ms before next ring
|
||||
start = utime.ticks_ms()
|
||||
while utime.ticks_diff(utime.ticks_ms(), start) < self.delay:
|
||||
pass
|
||||
radius += 1
|
||||
|
||||
# Radiate back out (darkness outward): turn off from center to edges
|
||||
last_radius = max(0, radius - 1)
|
||||
for r in range(0, last_radius + 1):
|
||||
for o in origins:
|
||||
left = o - r
|
||||
right = o + r
|
||||
if 0 <= left < self.num_leds:
|
||||
self.n[left] = (0, 0, 0)
|
||||
if 0 <= right < self.num_leds:
|
||||
self.n[right] = (0, 0, 0)
|
||||
self.n.write()
|
||||
start = utime.ticks_ms()
|
||||
while utime.ticks_diff(utime.ticks_ms(), start) < self.delay:
|
||||
pass
|
||||
|
||||
# ensure all LEDs are off at completion
|
||||
self.fill((0, 0, 0))
|
||||
# mark complete so scheduler won't auto-run again until re-selected
|
||||
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 ---")
|
||||
|
||||
|
||||
|
70
src/patterns_base.py
Normal file
70
src/patterns_base.py
Normal file
@@ -0,0 +1,70 @@
|
||||
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]))
|
@@ -9,21 +9,14 @@ class Settings(dict):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.load() # Load settings from file during initialization
|
||||
if self["color_order"] == "rbg": self.color_order = (1, 5, 3)
|
||||
if self.get("color_order", "rgb") == "rbg": self.color_order = (1, 5, 3)
|
||||
else: self.color_order = (1, 3, 5)
|
||||
|
||||
def set_defaults(self):
|
||||
self["led_pin"] = 10
|
||||
self["num_leds"] = 50
|
||||
self["pattern"] = "on"
|
||||
self["color1"] = "#00ff00"
|
||||
self["color2"] = "#ff0000"
|
||||
self["delay"] = 100
|
||||
self["brightness"] = 10
|
||||
self["led_pin"] = 4
|
||||
self["num_leds"] = 59
|
||||
self["color_order"] = "rgb"
|
||||
self["name"] = f"led-{ubinascii.hexlify(wifi.get_mac()).decode()}"
|
||||
self["ap_password"] = ""
|
||||
self["id"] = 0
|
||||
self["name"] = f"103"
|
||||
|
||||
def save(self):
|
||||
try:
|
||||
@@ -47,7 +40,6 @@ 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":
|
||||
@@ -70,6 +62,24 @@ 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()
|
||||
@@ -90,7 +100,8 @@ class Settings(dict):
|
||||
if save:
|
||||
self.save()
|
||||
return "OK", 200
|
||||
except (KeyError, ValueError):
|
||||
except Exception as e:
|
||||
print(f"An unexpected error occurred in set_settings: {e}")
|
||||
return "Bad request", 400
|
||||
|
||||
# Example usage
|
||||
|
158
test/main.py
Normal file
158
test/main.py
Normal file
@@ -0,0 +1,158 @@
|
||||
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", 127),
|
||||
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()
|
Reference in New Issue
Block a user