scrollkit 0.8.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. scrollkit/__init__.py +16 -0
  2. scrollkit/app/__init__.py +6 -0
  3. scrollkit/app/base.py +918 -0
  4. scrollkit/app/memory.py +70 -0
  5. scrollkit/config/__init__.py +1 -0
  6. scrollkit/config/settings_manager.py +215 -0
  7. scrollkit/config/transition_names.py +31 -0
  8. scrollkit/dev/__init__.py +41 -0
  9. scrollkit/dev/capabilities.py +374 -0
  10. scrollkit/dev/harness.py +383 -0
  11. scrollkit/dev/metrics.py +91 -0
  12. scrollkit/dev/performance.py +174 -0
  13. scrollkit/dev/validation.py +245 -0
  14. scrollkit/display/__init__.py +14 -0
  15. scrollkit/display/_graphics.py +299 -0
  16. scrollkit/display/_recording.py +165 -0
  17. scrollkit/display/_sim_backend.py +99 -0
  18. scrollkit/display/bitmap_text.py +408 -0
  19. scrollkit/display/boards.py +186 -0
  20. scrollkit/display/colors.py +176 -0
  21. scrollkit/display/content.py +604 -0
  22. scrollkit/display/gradient_text.py +167 -0
  23. scrollkit/display/interface.py +126 -0
  24. scrollkit/display/simulator.py +80 -0
  25. scrollkit/display/text_fill.py +59 -0
  26. scrollkit/display/text_pixels.py +250 -0
  27. scrollkit/display/unified.py +595 -0
  28. scrollkit/effects/__init__.py +28 -0
  29. scrollkit/effects/drip_splash.py +253 -0
  30. scrollkit/effects/easing.py +131 -0
  31. scrollkit/effects/overlay.py +92 -0
  32. scrollkit/effects/particles.py +355 -0
  33. scrollkit/effects/reveal_splash.py +132 -0
  34. scrollkit/effects/scrolling.py +363 -0
  35. scrollkit/effects/swarm_reveal.py +512 -0
  36. scrollkit/effects/text_render.py +25 -0
  37. scrollkit/effects/transitions.py +873 -0
  38. scrollkit/exceptions.py +55 -0
  39. scrollkit/network/__init__.py +1 -0
  40. scrollkit/network/http_client.py +505 -0
  41. scrollkit/network/mdns.py +44 -0
  42. scrollkit/network/wifi_manager.py +382 -0
  43. scrollkit/ota/__init__.py +13 -0
  44. scrollkit/ota/client.py +528 -0
  45. scrollkit/ota/display_progress.py +125 -0
  46. scrollkit/ota/manifest.py +206 -0
  47. scrollkit/ota/publish.py +379 -0
  48. scrollkit/simulator/ATTRIBUTION.md +20 -0
  49. scrollkit/simulator/CIRCUITPYTHON_COMPATIBILITY.md +364 -0
  50. scrollkit/simulator/LICENSE +176 -0
  51. scrollkit/simulator/README.md +87 -0
  52. scrollkit/simulator/__init__.py +11 -0
  53. scrollkit/simulator/adafruit_bitmap_font/__init__.py +5 -0
  54. scrollkit/simulator/adafruit_bitmap_font/bitmap_font.py +273 -0
  55. scrollkit/simulator/adafruit_bitmap_font/glyph_cache.py +70 -0
  56. scrollkit/simulator/adafruit_display_text/__init__.py +5 -0
  57. scrollkit/simulator/adafruit_display_text/label.py +336 -0
  58. scrollkit/simulator/bitmaptools.py +50 -0
  59. scrollkit/simulator/core/__init__.py +8 -0
  60. scrollkit/simulator/core/color_utils.py +62 -0
  61. scrollkit/simulator/core/device_benchmarks.json +254 -0
  62. scrollkit/simulator/core/feasibility.py +160 -0
  63. scrollkit/simulator/core/hardware_profile.py +191 -0
  64. scrollkit/simulator/core/led_matrix.py +307 -0
  65. scrollkit/simulator/core/matrixportal_s3_baseline.json +12 -0
  66. scrollkit/simulator/core/performance_manager.py +253 -0
  67. scrollkit/simulator/core/pixel_buffer.py +188 -0
  68. scrollkit/simulator/devices/__init__.py +7 -0
  69. scrollkit/simulator/devices/base_device.py +79 -0
  70. scrollkit/simulator/devices/matrixportal_s3.py +87 -0
  71. scrollkit/simulator/displayio/__init__.py +12 -0
  72. scrollkit/simulator/displayio/bitmap.py +158 -0
  73. scrollkit/simulator/displayio/display.py +195 -0
  74. scrollkit/simulator/displayio/fourwire.py +54 -0
  75. scrollkit/simulator/displayio/group.py +125 -0
  76. scrollkit/simulator/displayio/ondiskbitmap.py +109 -0
  77. scrollkit/simulator/displayio/palette.py +115 -0
  78. scrollkit/simulator/displayio/tilegrid.py +155 -0
  79. scrollkit/simulator/fonts/3x5.bdf +2474 -0
  80. scrollkit/simulator/fonts/Arial_16.bdf +7366 -0
  81. scrollkit/simulator/fonts/Arial_16.bdf.license +3 -0
  82. scrollkit/simulator/fonts/Arial_Bold_12.bdf +6131 -0
  83. scrollkit/simulator/fonts/Arial_Bold_12.bdf.license +2 -0
  84. scrollkit/simulator/fonts/Arial_Bold_18.bdf +32653 -0
  85. scrollkit/simulator/fonts/Arial_Bold_18.bdf.license +2 -0
  86. scrollkit/simulator/fonts/Junction_regular_24.bdf +8676 -0
  87. scrollkit/simulator/fonts/Junction_regular_24.bdf.license +3 -0
  88. scrollkit/simulator/fonts/LeagueSpartan-Bold-16.bdf +12458 -0
  89. scrollkit/simulator/fonts/LeagueSpartan-Bold-16.bdf.license +1921 -0
  90. scrollkit/simulator/fonts/LeagueSpartan_Bold_16.bdf +12458 -0
  91. scrollkit/simulator/fonts/LeagueSpartan_Bold_16.bdf.license +4 -0
  92. scrollkit/simulator/fonts/LibreBodoniv2002-Bold-27.bdf +16818 -0
  93. scrollkit/simulator/fonts/LibreBodoniv2002-Bold-27.bdf.license +1921 -0
  94. scrollkit/simulator/fonts/tom-thumb.bdf +2353 -0
  95. scrollkit/simulator/fonts/viii-bold.bdf +2673 -0
  96. scrollkit/simulator/fonts/viii.bdf +2659 -0
  97. scrollkit/simulator/terminalio/__init__.py +20 -0
  98. scrollkit/utils/__init__.py +1 -0
  99. scrollkit/utils/color_utils.py +54 -0
  100. scrollkit/utils/diagnostics.py +227 -0
  101. scrollkit/utils/error_handler.py +347 -0
  102. scrollkit/utils/system_utils.py +245 -0
  103. scrollkit/utils/url_utils.py +46 -0
  104. scrollkit/web/__init__.py +6 -0
  105. scrollkit/web/settings_server.py +328 -0
  106. scrollkit/web/wifi_setup.py +331 -0
  107. scrollkit-0.8.3.dist-info/METADATA +248 -0
  108. scrollkit-0.8.3.dist-info/RECORD +111 -0
  109. scrollkit-0.8.3.dist-info/WHEEL +5 -0
  110. scrollkit-0.8.3.dist-info/licenses/LICENSE +31 -0
  111. scrollkit-0.8.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,364 @@
1
+ # CircuitPython Compatibility Guide
2
+
3
+ This document outlines the CircuitPython compatibility considerations when using PyLEDSimulator and developing code that needs to run on both desktop (simulation) and CircuitPython hardware.
4
+
5
+ ## Overview
6
+
7
+ PyLEDSimulator implements CircuitPython's display APIs to allow desktop development and testing. However, there are important differences between standard Python and CircuitPython that developers must be aware of.
8
+
9
+ ## Core Compatibility Principles
10
+
11
+ ### 1. Module Availability
12
+
13
+ CircuitPython has a limited subset of Python's standard library. Many common modules are not available or have limited functionality.
14
+
15
+ #### Available in CircuitPython:
16
+ - `os` (limited functionality)
17
+ - `time` (limited functionality)
18
+ - `json` (basic functionality only)
19
+ - `re` (basic regex support)
20
+ - `random` (limited functionality - no `shuffle()`)
21
+ - `asyncio` (cooperative multitasking only)
22
+ - `gc` (garbage collection)
23
+ - `sys` (limited functionality)
24
+
25
+ #### NOT Available in CircuitPython:
26
+ - `pathlib`
27
+ - `urllib`
28
+ - `threading`
29
+ - `subprocess`
30
+ - `typing` (type hints must be removed)
31
+ - `dataclasses`
32
+ - `collections` (except `namedtuple`)
33
+ - Most standard library modules
34
+
35
+ ### 2. Exception Handling Differences
36
+
37
+ CircuitPython uses different exceptions than standard Python:
38
+
39
+ ```python
40
+ # ❌ WRONG - Standard Python only
41
+ try:
42
+ data = json.loads(response)
43
+ except json.JSONDecodeError: # Not available in CircuitPython
44
+ pass
45
+
46
+ # ✅ CORRECT - CircuitPython compatible
47
+ try:
48
+ data = json.loads(response)
49
+ except ValueError: # CircuitPython raises ValueError for invalid JSON
50
+ pass
51
+
52
+ # ❌ WRONG - Standard Python only
53
+ try:
54
+ with open('file.txt') as f:
55
+ content = f.read()
56
+ except FileNotFoundError: # Not available in CircuitPython
57
+ pass
58
+
59
+ # ✅ CORRECT - CircuitPython compatible
60
+ try:
61
+ with open('file.txt') as f:
62
+ content = f.read()
63
+ except OSError: # CircuitPython only has OSError
64
+ pass
65
+ ```
66
+
67
+ ### 3. String and Type Operations
68
+
69
+ ```python
70
+ # ❌ WRONG - f-string debug syntax not supported
71
+ print(f"{value=}")
72
+
73
+ # ✅ CORRECT - Use regular f-strings
74
+ print(f"value={value}")
75
+
76
+ # ❌ WRONG - match/case not available
77
+ match command:
78
+ case "start":
79
+ start()
80
+
81
+ # ✅ CORRECT - Use if/elif
82
+ if command == "start":
83
+ start()
84
+ elif command == "stop":
85
+ stop()
86
+ ```
87
+
88
+ ### 4. Memory Management
89
+
90
+ CircuitPython has limited memory (typically 256KB-512KB RAM):
91
+
92
+ ```python
93
+ # ❌ WRONG - Loading large data structures
94
+ data = json.loads(large_file_content) # May cause MemoryError
95
+
96
+ # ✅ CORRECT - Process data in chunks or use generators
97
+ def process_json_lines(file_path):
98
+ with open(file_path) as f:
99
+ for line in f:
100
+ yield json.loads(line)
101
+ ```
102
+
103
+ ### 5. File Operations
104
+
105
+ ```python
106
+ # ❌ WRONG - Using pathlib
107
+ from pathlib import Path
108
+ if Path('config.json').exists():
109
+ pass
110
+
111
+ # ✅ CORRECT - Use os or try/except
112
+ import os
113
+ try:
114
+ os.stat('config.json')
115
+ file_exists = True
116
+ except OSError:
117
+ file_exists = False
118
+ ```
119
+
120
+ ### 6. Time Operations
121
+
122
+ ```python
123
+ # ❌ WRONG - time.time() not always available
124
+ start_time = time.time()
125
+
126
+ # ✅ CORRECT - Use time.monotonic()
127
+ start_time = time.monotonic()
128
+ ```
129
+
130
+ ### 7. Random Module Limitations
131
+
132
+ ```python
133
+ # ❌ WRONG - random.shuffle() not available
134
+ import random
135
+ my_list = [1, 2, 3, 4, 5]
136
+ random.shuffle(my_list)
137
+
138
+ # ✅ CORRECT - Implement simple shuffle
139
+ import random
140
+
141
+ def simple_shuffle(lst):
142
+ """Simple in-place shuffle for CircuitPython compatibility."""
143
+ for i in range(len(lst)):
144
+ j = random.randint(0, len(lst) - 1)
145
+ lst[i], lst[j] = lst[j], lst[i]
146
+
147
+ my_list = [1, 2, 3, 4, 5]
148
+ simple_shuffle(my_list)
149
+ ```
150
+
151
+ ### 8. Async Operations
152
+
153
+ ```python
154
+ # ❌ WRONG - asyncio.run() at module level
155
+ import asyncio
156
+ asyncio.run(main())
157
+
158
+ # ✅ CORRECT - Use asyncio properly
159
+ import asyncio
160
+
161
+ async def main():
162
+ # Your async code here
163
+ pass
164
+
165
+ # At the bottom of your main file:
166
+ if __name__ == "__main__":
167
+ asyncio.run(main())
168
+ ```
169
+
170
+ ## displayio API Changes
171
+
172
+ ### Display.show() Removed in CircuitPython 9.x
173
+
174
+ The `.show()` method has been removed from Display objects in CircuitPython 9.x:
175
+
176
+ ```python
177
+ # ❌ WRONG - Old API (CircuitPython 8.x and earlier)
178
+ display.show(None) # Clear display
179
+ display.show(group) # Show group
180
+
181
+ # ✅ CORRECT - New API (CircuitPython 9.x)
182
+ display.root_group = None # Clear display
183
+ display.root_group = group # Show group
184
+ ```
185
+
186
+ ### MatrixPortal Pixel Access
187
+
188
+ The MatrixPortal class doesn't provide direct pixel manipulation methods:
189
+
190
+ ```python
191
+ # ❌ WRONG - No set_pixel/get_pixel methods
192
+ matrixportal.set_pixel(x, y, color)
193
+ color = matrixportal.get_pixel(x, y)
194
+
195
+ # ✅ CORRECT - Use displayio.Bitmap
196
+ bitmap = displayio.Bitmap(width, height, num_colors)
197
+ palette = displayio.Palette(num_colors)
198
+ palette[0] = 0x000000 # Black
199
+ palette[1] = 0xFFFF00 # Yellow
200
+
201
+ tile_grid = displayio.TileGrid(bitmap, pixel_shader=palette)
202
+ group = displayio.Group()
203
+ group.append(tile_grid)
204
+ display.root_group = group
205
+
206
+ # Set pixels via bitmap
207
+ bitmap[x, y] = 1 # Set to color index 1 (yellow)
208
+ ```
209
+
210
+ ## PyLEDSimulator-Specific Compatibility
211
+
212
+ ### 1. Conditional Imports
213
+
214
+ Always structure imports to support both environments:
215
+
216
+ ```python
217
+ try:
218
+ # Try hardware imports first
219
+ import board
220
+ import displayio
221
+ import terminalio
222
+ from adafruit_display_text import label
223
+ HARDWARE_AVAILABLE = True
224
+ except ImportError:
225
+ # Fall back to simulator
226
+ from pyledsimulator import displayio
227
+ from pyledsimulator import terminalio
228
+ from pyledsimulator.adafruit_display_text import label
229
+ HARDWARE_AVAILABLE = False
230
+ ```
231
+
232
+ ### 2. Display Initialization
233
+
234
+ ```python
235
+ def get_display():
236
+ """Get display for hardware or simulator."""
237
+ if HARDWARE_AVAILABLE:
238
+ return board.DISPLAY
239
+ else:
240
+ from pyledsimulator.devices import MatrixPortalS3
241
+ device = MatrixPortalS3()
242
+ device.initialize()
243
+ return device.display
244
+ ```
245
+
246
+ ### 3. Performance Differences
247
+
248
+ The simulator runs at desktop speeds, which is much faster than CircuitPython:
249
+
250
+ ```python
251
+ # Add delays to simulate hardware timing
252
+ if not HARDWARE_AVAILABLE:
253
+ time.sleep(0.1) # Simulate hardware processing time
254
+ ```
255
+
256
+ ### 4. Memory Constraints Simulation
257
+
258
+ When developing with the simulator, be mindful of hardware memory limits:
259
+
260
+ ```python
261
+ # Even though desktop has more memory, design for hardware constraints
262
+ MAX_SPRITES = 10 # Limit based on hardware capabilities
263
+ MAX_TEXT_LENGTH = 100 # Avoid long strings
264
+ ```
265
+
266
+ ## Common Pitfalls and Solutions
267
+
268
+ ### 1. HTTP Libraries
269
+
270
+ ```python
271
+ # ❌ WRONG - Using requests
272
+ import requests
273
+ response = requests.get(url)
274
+
275
+ # ✅ CORRECT - Use adafruit_requests
276
+ import adafruit_requests
277
+ response = adafruit_requests.get(url)
278
+ ```
279
+
280
+ ### 2. JSON Handling
281
+
282
+ ```python
283
+ # ❌ WRONG - Expecting specific exceptions
284
+ try:
285
+ data = json.loads(text)
286
+ except json.JSONDecodeError as e:
287
+ print(f"JSON error at position {e.pos}")
288
+
289
+ # ✅ CORRECT - Handle generic ValueError
290
+ try:
291
+ data = json.loads(text)
292
+ except ValueError as e:
293
+ print(f"JSON error: {e}")
294
+ ```
295
+
296
+ ### 3. Type Hints
297
+
298
+ ```python
299
+ # ❌ WRONG - Using type hints
300
+ def process_data(data: dict[str, Any]) -> list[str]:
301
+ pass
302
+
303
+ # ✅ CORRECT - Remove type hints or use comments
304
+ def process_data(data): # type: (dict) -> list
305
+ """Process data dictionary and return list of strings."""
306
+ pass
307
+ ```
308
+
309
+ ## Testing for Compatibility
310
+
311
+ ### 1. Linting for CircuitPython
312
+
313
+ Use the project's linting tools to catch compatibility issues:
314
+
315
+ ```bash
316
+ make lint-errors # Catches undefined names and syntax errors
317
+ ```
318
+
319
+ ### 2. Import Testing
320
+
321
+ Test imports in both environments:
322
+
323
+ ```python
324
+ # test_imports.py
325
+ def test_imports():
326
+ """Verify all imports work in current environment."""
327
+ try:
328
+ import json
329
+ import time
330
+ import os
331
+ print("✓ Core imports successful")
332
+ except ImportError as e:
333
+ print(f"✗ Import failed: {e}")
334
+ ```
335
+
336
+ ### 3. Memory Testing
337
+
338
+ ```python
339
+ import gc
340
+
341
+ def check_memory():
342
+ """Check available memory (CircuitPython only)."""
343
+ gc.collect()
344
+ if hasattr(gc, 'mem_free'):
345
+ print(f"Free memory: {gc.mem_free()} bytes")
346
+ ```
347
+
348
+ ## Best Practices
349
+
350
+ 1. **Always test on hardware**: The simulator is for development, but final testing must be on actual hardware
351
+ 2. **Use minimal dependencies**: Stick to CircuitPython built-ins when possible
352
+ 3. **Handle errors gracefully**: Assume operations can fail due to memory or hardware constraints
353
+ 4. **Keep it simple**: Complex Python features are often not available
354
+ 5. **Check CircuitPython docs**: When in doubt, consult https://docs.circuitpython.org/
355
+
356
+ ## Useful Resources
357
+
358
+ - [CircuitPython API Reference](https://docs.circuitpython.org/en/latest/docs/library/index.html)
359
+ - [CircuitPython vs MicroPython](https://docs.circuitpython.org/en/latest/docs/design_guide.html)
360
+ - [Adafruit Learn Guides](https://learn.adafruit.com/category/circuitpython)
361
+
362
+ ## Version Compatibility
363
+
364
+ PyLEDSimulator targets CircuitPython 8.x and 9.x compatibility. Features specific to newer versions should be avoided or have fallbacks.
@@ -0,0 +1,176 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
@@ -0,0 +1,87 @@
1
+ # LED Simulator
2
+
3
+ A standalone Python library for simulating LED matrix displays used with CircuitPython boards. The library provides a visual representation of LED hardware using Pygame, allowing developers to test their code on desktop platforms before deploying to actual hardware.
4
+
5
+ ## Features
6
+
7
+ - **Pixel-Perfect LED Simulation**: Realistic LED rendering with configurable pitch (2.5, 3, 4, 5, 6mm)
8
+ - **CircuitPython API Compatibility**: Implements displayio, terminalio, and adafruit_display_text APIs
9
+ - **Multi-Display Support**: Tile multiple displays together (up to 4x4 grid)
10
+ - **Font Support**: BDF font loading with bundled fonts
11
+ - **Performance Simulation**: Optional hardware performance characteristics simulation
12
+ - **Device Emulation**: Pre-configured device profiles (MatrixPortal S3, etc.)
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ pip install led_simulator
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ```python
23
+ from led_simulator.devices import MatrixPortalS3
24
+ from led_simulator.displayio import Group
25
+ from led_simulator.adafruit_display_text import Label
26
+ from led_simulator.terminalio import FONT
27
+
28
+ # Create device
29
+ device = MatrixPortalS3()
30
+ device.initialize()
31
+
32
+ # Create text label
33
+ text_group = Group()
34
+ label = Label(font=FONT, text="Hello LED!")
35
+ label.x = 0
36
+ label.y = 15
37
+ text_group.append(label)
38
+
39
+ # Show on display
40
+ device.display.show(text_group)
41
+
42
+ # Run the simulation
43
+ device.run()
44
+ ```
45
+
46
+ ## API Compatibility
47
+
48
+ PyLEDSimulator aims for "sufficient API compatibility" with CircuitPython's displayio module. This means:
49
+ - Your CircuitPython code should work with minimal or no modifications
50
+ - The visual output closely matches real hardware
51
+ - Some advanced features may have behavioral differences (documented below)
52
+
53
+ ### Known Limitations
54
+
55
+ - Rendering order nuances may differ slightly from hardware
56
+ - Memory management follows Python's model, not CircuitPython's
57
+ - Some rotation/scaling transforms may have minor differences
58
+ - Font rendering may have slight metric differences
59
+
60
+ ### CircuitPython Compatibility
61
+
62
+ For detailed information about writing code that works in both the simulator and on CircuitPython hardware, see [CIRCUITPYTHON_COMPATIBILITY.md](CIRCUITPYTHON_COMPATIBILITY.md). This guide covers:
63
+ - Module availability differences
64
+ - Exception handling in CircuitPython
65
+ - Memory constraints
66
+ - Common pitfalls and solutions
67
+
68
+ ## Examples
69
+
70
+ See the `examples/` directory for various usage examples:
71
+ - `matrixportal_s3_example.py` - Basic MatrixPortal S3 simulation
72
+ - `scrolling_text_example.py` - Text scrolling demonstration
73
+ - `dual_zone_example.py` - Multi-zone display example
74
+ - `multi_display_example.py` - Multiple display coordination
75
+ - `rainbow_text.py` - Colorful text effects
76
+ - `pixel_dust.py` - Particle effects simulation
77
+
78
+ ## License
79
+
80
+ Apache License 2.0
81
+
82
+ ## Attribution
83
+
84
+ Portions of this project are adapted from CircuitPython:
85
+ - Copyright (c) 2016-2024 Adafruit Industries
86
+ - Licensed under MIT License
87
+ - See ATTRIBUTION.md for details
@@ -0,0 +1,11 @@
1
+ # Copyright (c) 2024-2026 Michael Winslow Czeiszperger
2
+ """SLDK LED Matrix Simulator."""
3
+
4
+ # Make simulator modules available at package level
5
+ from . import core
6
+ from . import devices
7
+ from . import displayio
8
+ from . import adafruit_display_text
9
+ from . import terminalio
10
+
11
+ __version__ = "0.8.1"
@@ -0,0 +1,5 @@
1
+ """Bitmap font support."""
2
+
3
+ from .bitmap_font import load_font
4
+
5
+ __all__ = ['load_font']