IS-Matrix-Forge 1.0.0.dev1__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 (123) hide show
  1. is_matrix_forge/__init__.py +8 -0
  2. is_matrix_forge/assets/__init__.py +8 -0
  3. is_matrix_forge/assets/audio/__init__.py +36 -0
  4. is_matrix_forge/assets/audio/files/__init__.py +53 -0
  5. is_matrix_forge/assets/audio/files/plugged.wav +0 -0
  6. is_matrix_forge/assets/audio/files/unplugged.wav +0 -0
  7. is_matrix_forge/assets/char_map.json +2150 -0
  8. is_matrix_forge/assets/font_map.py +119 -0
  9. is_matrix_forge/common/__init__.py +0 -0
  10. is_matrix_forge/common/decorators/__init__.py +152 -0
  11. is_matrix_forge/common/dirs.py +20 -0
  12. is_matrix_forge/common/helpers/__init__.py +132 -0
  13. is_matrix_forge/common/helpers/github_api.py +67 -0
  14. is_matrix_forge/designer_gui/__init__.py +8 -0
  15. is_matrix_forge/designer_gui/main_window/__init__.py +252 -0
  16. is_matrix_forge/designer_gui/main_window/layout.py +117 -0
  17. is_matrix_forge/dev_tools/__init__.py +8 -0
  18. is_matrix_forge/dev_tools/debug.py +82 -0
  19. is_matrix_forge/dev_tools/presets.py +47 -0
  20. is_matrix_forge/inputmodule/__init__.py +164 -0
  21. is_matrix_forge/inputmodule/b1display.py +160 -0
  22. is_matrix_forge/inputmodule/c1minimal.py +35 -0
  23. is_matrix_forge/inputmodule/font.py +2166 -0
  24. is_matrix_forge/inputmodule/helpers/__init__.py +16 -0
  25. is_matrix_forge/inputmodule/ledmatrix.py +516 -0
  26. is_matrix_forge/led_matrix/Scripts/__init__.py +4 -0
  27. is_matrix_forge/led_matrix/Scripts/battery_monitor.py +152 -0
  28. is_matrix_forge/led_matrix/Scripts/install_presets/__init__.py +2 -0
  29. is_matrix_forge/led_matrix/Scripts/install_presets/arguments.py +84 -0
  30. is_matrix_forge/led_matrix/Scripts/install_presets/main.py +192 -0
  31. is_matrix_forge/led_matrix/__init__.py +7 -0
  32. is_matrix_forge/led_matrix/commands/__init__.py +84 -0
  33. is_matrix_forge/led_matrix/commands/map.py +46 -0
  34. is_matrix_forge/led_matrix/constants.py +82 -0
  35. is_matrix_forge/led_matrix/controller/__init__.py +26 -0
  36. is_matrix_forge/led_matrix/controller/controller.py +635 -0
  37. is_matrix_forge/led_matrix/controller/helpers/__init__.py +7 -0
  38. is_matrix_forge/led_matrix/controller/helpers/threading.py +41 -0
  39. is_matrix_forge/led_matrix/controller/multiton.py +32 -0
  40. is_matrix_forge/led_matrix/controller/threaded.py +72 -0
  41. is_matrix_forge/led_matrix/display/__init__.py +13 -0
  42. is_matrix_forge/led_matrix/display/animations/__init__.py +29 -0
  43. is_matrix_forge/led_matrix/display/animations/animation.py +593 -0
  44. is_matrix_forge/led_matrix/display/animations/effects/__init__.py +17 -0
  45. is_matrix_forge/led_matrix/display/animations/errors.py +23 -0
  46. is_matrix_forge/led_matrix/display/animations/font/__init__.py +17 -0
  47. is_matrix_forge/led_matrix/display/animations/font/font.py +4 -0
  48. is_matrix_forge/led_matrix/display/animations/font/map_builder.py +123 -0
  49. is_matrix_forge/led_matrix/display/animations/frame/__init__.py +47 -0
  50. is_matrix_forge/led_matrix/display/animations/frame/base.py +245 -0
  51. is_matrix_forge/led_matrix/display/animations/frame/helpers.py +67 -0
  52. is_matrix_forge/led_matrix/display/animations/keep_awake.py +1 -0
  53. is_matrix_forge/led_matrix/display/animations/plugged.py +0 -0
  54. is_matrix_forge/led_matrix/display/animations/text_scroller.py +104 -0
  55. is_matrix_forge/led_matrix/display/effects/__init__.py +17 -0
  56. is_matrix_forge/led_matrix/display/effects/breather.py +206 -0
  57. is_matrix_forge/led_matrix/display/effects/fade.py +0 -0
  58. is_matrix_forge/led_matrix/display/effects/scroller/__init__.py +92 -0
  59. is_matrix_forge/led_matrix/display/effects/scroller/base.py +76 -0
  60. is_matrix_forge/led_matrix/display/effects/scroller/helpers.py +62 -0
  61. is_matrix_forge/led_matrix/display/grid/__init__.py +9 -0
  62. is_matrix_forge/led_matrix/display/grid/grid.py +208 -0
  63. is_matrix_forge/led_matrix/display/grid/helpers.py +130 -0
  64. is_matrix_forge/led_matrix/display/grid/presets/__init__.py +64 -0
  65. is_matrix_forge/led_matrix/display/grid/presets/manifest.py +172 -0
  66. is_matrix_forge/led_matrix/display/helpers/__init__.py +110 -0
  67. is_matrix_forge/led_matrix/display/helpers/columns.py +33 -0
  68. is_matrix_forge/led_matrix/display/media.py +199 -0
  69. is_matrix_forge/led_matrix/display/patterns/__init__.py +59 -0
  70. is_matrix_forge/led_matrix/display/patterns/built_in/__init__.py +91 -0
  71. is_matrix_forge/led_matrix/display/patterns/built_in/commands.py +3 -0
  72. is_matrix_forge/led_matrix/display/patterns/built_in/constants.py +29 -0
  73. is_matrix_forge/led_matrix/display/patterns/built_in/stencils.py +70 -0
  74. is_matrix_forge/led_matrix/display/patterns/utils.py +2 -0
  75. is_matrix_forge/led_matrix/display/text/__init__.py +66 -0
  76. is_matrix_forge/led_matrix/display/text/scroller.py +136 -0
  77. is_matrix_forge/led_matrix/errors/__init__.py +4 -0
  78. is_matrix_forge/led_matrix/errors/base.py +14 -0
  79. is_matrix_forge/led_matrix/errors/grid.py +37 -0
  80. is_matrix_forge/led_matrix/errors/matrix.py +68 -0
  81. is_matrix_forge/led_matrix/errors/misc.py +36 -0
  82. is_matrix_forge/led_matrix/fonts.py +20 -0
  83. is_matrix_forge/led_matrix/hardware.py +172 -0
  84. is_matrix_forge/led_matrix/helpers/__init__.py +165 -0
  85. is_matrix_forge/led_matrix/helpers/core/__init__.py +17 -0
  86. is_matrix_forge/led_matrix/helpers/core/lazy_mod_load.py +84 -0
  87. is_matrix_forge/led_matrix/helpers/device.py +118 -0
  88. is_matrix_forge/led_matrix/helpers/status_handler.py +14 -0
  89. is_matrix_forge/led_matrix/helpers/stream/__init__.py +70 -0
  90. is_matrix_forge/led_matrix/helpers/stream/errors.py +17 -0
  91. is_matrix_forge/led_matrix/led_matrix.py +25 -0
  92. is_matrix_forge/led_matrix/status.py +8 -0
  93. is_matrix_forge/led_matrix/tools/__init__.py +8 -0
  94. is_matrix_forge/led_matrix/tools/identify.py +27 -0
  95. is_matrix_forge/led_matrix/utils/__init__.py +41 -0
  96. is_matrix_forge/log_engine.py +25 -0
  97. is_matrix_forge/monitor/__init__.py +68 -0
  98. is_matrix_forge/monitor/errors.py +35 -0
  99. is_matrix_forge/monitor/events.py +78 -0
  100. is_matrix_forge/monitor/gui/__init__.py +0 -0
  101. is_matrix_forge/monitor/gui/errors.py +35 -0
  102. is_matrix_forge/monitor/gui/event/__init__.py +8 -0
  103. is_matrix_forge/monitor/gui/event/collection.py +153 -0
  104. is_matrix_forge/monitor/gui/event/errors.py +58 -0
  105. is_matrix_forge/monitor/gui/event/event.py +226 -0
  106. is_matrix_forge/monitor/gui/layout/__init__.py +8 -0
  107. is_matrix_forge/monitor/gui/layout/base.py +147 -0
  108. is_matrix_forge/monitor/gui/metaclasses/__init__.py +20 -0
  109. is_matrix_forge/monitor/gui/windows/__init__.py +0 -0
  110. is_matrix_forge/monitor/gui/windows/base.py +325 -0
  111. is_matrix_forge/monitor/gui/windows/main/__init__.py +77 -0
  112. is_matrix_forge/monitor/gui/windows/main/layout.py +21 -0
  113. is_matrix_forge/monitor/helpers.py +55 -0
  114. is_matrix_forge/monitor/monitor.py +431 -0
  115. is_matrix_forge/notify/__init__.py +0 -0
  116. is_matrix_forge/notify/sounds/__init__.py +6 -0
  117. is_matrix_forge/notify/sounds/base.py +106 -0
  118. is_matrix_forge/progress.py +99 -0
  119. is_matrix_forge-1.0.0.dev1.dist-info/LICENSE.md +19 -0
  120. is_matrix_forge-1.0.0.dev1.dist-info/METADATA +190 -0
  121. is_matrix_forge-1.0.0.dev1.dist-info/RECORD +123 -0
  122. is_matrix_forge-1.0.0.dev1.dist-info/WHEEL +4 -0
  123. is_matrix_forge-1.0.0.dev1.dist-info/entry_points.txt +5 -0
@@ -0,0 +1,199 @@
1
+ """
2
+ Media display module for LED Matrix.
3
+
4
+ This module provides functions for displaying images and videos on the LED matrix.
5
+ It includes functions for rendering images, playing videos, and capturing from a camera.
6
+ """
7
+
8
+ import serial
9
+ import time
10
+ import cv2
11
+ from PIL import Image
12
+
13
+ from ..constants import WIDTH, HEIGHT
14
+ from ..hardware import send_serial
15
+ from ..commands.map import CommandVals
16
+ from ..commands import send_command
17
+ from . import send_col, commit_cols
18
+ from is_matrix_forge.led_matrix.helpers.status_handler import get_status, set_status
19
+
20
+
21
+ def image(dev, image_file):
22
+ """Display a black/white image
23
+ Send everything in a single command"""
24
+ # Initialize a byte array to hold the binary representation of the image
25
+ # 39 bytes = 312 bits, which is enough for 9x34 = 306 pixels
26
+ vals = [0x00 for _ in range(39)]
27
+
28
+ # Open and convert the image to RGB format
29
+ im = Image.open(image_file).convert("RGB")
30
+ width, height = im.size
31
+
32
+ # Verify image dimensions match the LED matrix size
33
+ assert width == 9
34
+ assert height == 34
35
+
36
+ # Get all pixel values as a flat list
37
+ pixel_values = list(im.getdata())
38
+
39
+ # Process each pixel
40
+ for i, pixel in enumerate(pixel_values):
41
+ # Calculate average brightness of RGB components
42
+ brightness = sum(pixel) / 3
43
+
44
+ # If brightness is above half of max (127.5), consider it "on"
45
+ # This creates a binary black/white effect
46
+ if brightness > 0xFF / 2:
47
+ # Calculate which byte in vals array this pixel belongs to (i/8)
48
+ # Then set the appropriate bit within that byte (i%8)
49
+ # This packs 8 pixels into each byte of the vals array
50
+ vals[int(i / 8)] |= 1 << i % 8
51
+
52
+ # Send the packed binary data to the device
53
+ send_command(dev, CommandVals.Draw, vals)
54
+
55
+
56
+ def pixel_to_brightness(pixel):
57
+ """Calculate pixel brightness from an RGB triple"""
58
+ assert len(pixel) == 3
59
+ brightness = sum(pixel) / len(pixel)
60
+
61
+ # Poor man's scaling to make the greyscale pop better.
62
+ # Should find a good function.
63
+ if brightness > 200:
64
+ brightness = brightness
65
+ elif brightness > 150:
66
+ brightness = brightness * 0.8
67
+ elif brightness > 100:
68
+ brightness = brightness * 0.5
69
+ elif brightness > 50:
70
+ brightness = brightness
71
+ else:
72
+ brightness = brightness * 2
73
+
74
+ return int(brightness)
75
+
76
+
77
+ def image_greyscale(dev, image_file):
78
+ """Display an image in greyscale
79
+ Sends each 1x34 column and then commits => 10 commands
80
+ """
81
+ with serial.Serial(dev.device, 115200) as s:
82
+ from PIL import Image
83
+
84
+ im = Image.open(image_file).convert("RGB")
85
+ width, height = im.size
86
+ assert width == 9
87
+ assert height == 34
88
+ pixel_values = list(im.getdata())
89
+ for x in range(0, WIDTH):
90
+ vals = [0 for _ in range(HEIGHT)]
91
+
92
+ for y in range(HEIGHT):
93
+ vals[y] = pixel_to_brightness(pixel_values[x + y * WIDTH])
94
+
95
+ send_col(dev, s, x, vals)
96
+ commit_cols(dev, s)
97
+
98
+
99
+ def camera(dev):
100
+ """Play a live view from the webcam, for fun"""
101
+ set_status('camera')
102
+ with serial.Serial(dev.device, 115200) as s:
103
+ import cv2
104
+
105
+ capture = cv2.VideoCapture(1)
106
+ ret, frame = capture.read()
107
+
108
+ scale_y = HEIGHT / frame.shape[0]
109
+
110
+ # Scale the video to 34 pixels height
111
+ dim = (HEIGHT, int(round(frame.shape[1] * scale_y)))
112
+ # Find the starting position to crop the width to be centered
113
+ # For very narrow videos, make sure to stay in bounds
114
+ start_x = max(0, int(round(dim[1] / 2 - WIDTH / 2)))
115
+ end_x = min(dim[1], start_x + WIDTH)
116
+
117
+ # Pre-process the video into resized, cropped, grayscale frames
118
+ while get_status() == 'camera':
119
+ ret, frame = capture.read()
120
+ if not ret:
121
+ print("Failed to capture video frames")
122
+ break
123
+
124
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
125
+
126
+ resized = cv2.resize(gray, (dim[1], dim[0]))
127
+ cropped = resized[0:HEIGHT, start_x:end_x]
128
+
129
+ for x in range(0, cropped.shape[1]):
130
+ vals = [0 for _ in range(HEIGHT)]
131
+
132
+ for y in range(0, HEIGHT):
133
+ vals[y] = cropped[y, x]
134
+
135
+ send_col(dev, s, x, vals)
136
+ commit_cols(dev, s)
137
+
138
+
139
+ def video(dev, video_file):
140
+ """Resize and play back a video"""
141
+ set_status('video')
142
+ with serial.Serial(dev.device, 115200) as s:
143
+ import cv2
144
+
145
+ capture = cv2.VideoCapture(video_file)
146
+ ret, frame = capture.read()
147
+
148
+ scale_y = HEIGHT / frame.shape[0]
149
+
150
+ # Scale the video to 34 pixels height
151
+ dim = (HEIGHT, int(round(frame.shape[1] * scale_y)))
152
+ # Find the starting position to crop the width to be centered
153
+ # For very narrow videos, make sure to stay in bounds
154
+ start_x = max(0, int(round(dim[1] / 2 - WIDTH / 2)))
155
+ end_x = min(dim[1], start_x + WIDTH)
156
+
157
+ processed = []
158
+
159
+ # Pre-process the video into resized, cropped, grayscale frames
160
+ while get_status() == 'video':
161
+ ret, frame = capture.read()
162
+ if not ret:
163
+ print("Failed to read video frames")
164
+ break
165
+
166
+ gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
167
+
168
+ resized = cv2.resize(gray, (dim[1], dim[0]))
169
+ cropped = resized[0:HEIGHT, start_x:end_x]
170
+
171
+ processed.append(cropped)
172
+
173
+ # Determine frame delay based on the video's FPS. Fall back to 30 FPS
174
+ # if the FPS value cannot be obtained.
175
+ fps = capture.get(cv2.CAP_PROP_FPS)
176
+ try:
177
+ fps = float(fps)
178
+ if fps <= 0 or fps != fps:
179
+ raise ValueError
180
+ except Exception:
181
+ fps = 30.0
182
+ frame_delay = 1.0 / fps
183
+
184
+ # Write it out to the module one frame at a time while respecting the
185
+ # original frame rate.
186
+ for frame in processed:
187
+ start = time.time()
188
+ for x in range(0, cropped.shape[1]):
189
+ vals = [0 for _ in range(HEIGHT)]
190
+
191
+ for y in range(0, HEIGHT):
192
+ vals[y] = frame[y, x]
193
+
194
+ send_col(dev, s, x, vals)
195
+ commit_cols(dev, s)
196
+
197
+ elapsed = time.time() - start
198
+ if frame_delay > elapsed:
199
+ time.sleep(frame_delay - elapsed)
@@ -0,0 +1,59 @@
1
+ """
2
+ Pattern display module for LED Matrix.
3
+
4
+ This module provides functions for displaying various patterns on the LED matrix.
5
+ It includes functions for displaying checkerboards, gradients, and other visual patterns.
6
+ """
7
+
8
+ import time
9
+
10
+ from .built_in.stencils import every_nth_row, every_nth_col
11
+ from ..helpers import render_matrix
12
+ from is_matrix_forge.led_matrix.display.patterns.built_in.stencils import every_nth_row, every_nth_col
13
+ from is_matrix_forge.led_matrix.display.helpers.columns import send_col
14
+
15
+
16
+ def breathing(dev):
17
+ """Animate breathing brightness.
18
+ Keeps currently displayed grid"""
19
+ set_status('breathing')
20
+ # Bright ranges appear similar, so we have to go through those faster
21
+ while get_status() == 'breathing':
22
+ # Go quickly from 250 to 50
23
+ for i in range(10):
24
+ time.sleep(0.03)
25
+ brightness(dev, 250 - i * 20)
26
+
27
+ # Go slowly from 50 to 0
28
+ for i in range(10):
29
+ time.sleep(0.06)
30
+ brightness(dev, 50 - i * 5)
31
+
32
+ # Go slowly from 0 to 50
33
+ for i in range(10):
34
+ time.sleep(0.06)
35
+ brightness(dev, i * 5)
36
+
37
+ # Go quickly from 50 to 250
38
+ for i in range(10):
39
+ time.sleep(0.03)
40
+ brightness(dev, 50 + i * 20)
41
+
42
+
43
+ def eq(dev, vals):
44
+ """Display 9 values in equalizer diagram starting from the middle, going up and down"""
45
+ matrix = [[0 for _ in range(34)] for _ in range(9)]
46
+
47
+ for col, val in enumerate(vals[:9]):
48
+ row = int(34 / 2)
49
+ above = int(val / 2)
50
+ below = val - above
51
+
52
+ for i in range(above):
53
+ matrix[col][row + i] = 0xFF
54
+ for i in range(below):
55
+ matrix[col][row - 1 - i] = 0xFF
56
+
57
+ render_matrix(dev, matrix)
58
+
59
+
@@ -0,0 +1,91 @@
1
+ import difflib
2
+ from typing import Any
3
+
4
+ from .constants import PATTERN_MAP
5
+
6
+
7
+ class BuiltInPatterns:
8
+ """
9
+ Interface for rendering built-in LED matrix patterns on a device.
10
+
11
+ Parameters
12
+ ----------
13
+ dev : Any
14
+ The device instance to send commands to.
15
+ """
16
+
17
+ def __init__(self, dev: Any):
18
+ from serial.tools.list_ports_common import ListPortInfo
19
+ from is_matrix_forge.led_matrix.controller.controller import LEDMatrixController
20
+
21
+ if dev is None:
22
+ from is_matrix_forge.led_matrix.controller.helpers import get_controllers
23
+
24
+ if not isinstance(dev, ListPortInfo) and not isinstance(dev, LEDMatrixController):
25
+ raise TypeError(f'dev must be of type `ListPortInfo` or `LEDMatrixController`, not {type(dev)}')
26
+ elif isinstance(dev, LEDMatrixController):
27
+ dev = dev.device
28
+
29
+ self.dev = dev
30
+
31
+ def render(self, pattern_name: str) -> None:
32
+ """
33
+ Render the specified pattern on the device.
34
+
35
+ Parameters
36
+ ----------
37
+ pattern_name : str
38
+ The name of the pattern to render.
39
+ """
40
+ normalized = self._normalize(pattern_name)
41
+ action = PATTERN_MAP.get(normalized)
42
+
43
+ if action:
44
+ action(self.dev)
45
+ else:
46
+ print(f"❌ Invalid pattern: '{pattern_name}'")
47
+ suggestions = self.suggest(pattern_name)
48
+ if suggestions:
49
+ print("💡 Did you mean:")
50
+ for s in suggestions:
51
+ print(f" - {s}")
52
+ else:
53
+ print("ℹ️ Available patterns:")
54
+ for key in self.list():
55
+ print(f" - {key}")
56
+
57
+ def suggest(self, pattern_name: str, n: int = 3) -> list[str]:
58
+ """
59
+ Suggest similar pattern names.
60
+
61
+ Parameters
62
+ ----------
63
+ pattern_name : str
64
+ The name to suggest alternatives for.
65
+
66
+ n : int, optional
67
+ Maximum number of suggestions to return.
68
+
69
+ Returns
70
+ -------
71
+ list[str]
72
+ List of suggested pattern names.
73
+ """
74
+ return difflib.get_close_matches(pattern_name, self.list(), n=n)
75
+
76
+ @staticmethod
77
+ def list() -> list[str]:
78
+ """
79
+ List all available pattern names.
80
+
81
+ Returns
82
+ -------
83
+ list[str]
84
+ Names of all patterns available.
85
+ """
86
+ return list(PATTERN_MAP.keys())
87
+
88
+ @staticmethod
89
+ def _normalize(name: str) -> str:
90
+ return name.strip()
91
+
@@ -0,0 +1,3 @@
1
+ from is_matrix_forge.led_matrix.hardware import PatternVals
2
+ from is_matrix_forge.led_matrix.commands.map import CommandVals
3
+ from is_matrix_forge.led_matrix.commands import send_command
@@ -0,0 +1,29 @@
1
+ from .commands import PatternVals
2
+ from is_matrix_forge.led_matrix.commands.map import CommandVals
3
+ from is_matrix_forge.led_matrix.commands import send_command
4
+ from .stencils import all_brightnesses, every_nth_row, every_nth_col, checkerboard
5
+
6
+
7
+ PATTERN_MAP = {
8
+ "All LEDs on": lambda dev: send_command(dev, CommandVals.Pattern, [PatternVals.FullBrightness]),
9
+ "Gradient (0-13% Brightness)": lambda dev: send_command(dev, CommandVals.Pattern, [PatternVals.Gradient]),
10
+ "Double Gradient (0-7-0% Brightness)": lambda dev: send_command(dev, CommandVals.Pattern, [PatternVals.DoubleGradient]),
11
+ "\"LOTUS\" sideways": lambda dev: send_command(dev, CommandVals.Pattern, [PatternVals.DisplayLotus]),
12
+ "Zigzag": lambda dev: send_command(dev, CommandVals.Pattern, [PatternVals.ZigZag]),
13
+ "\"PANIC\"": lambda dev: send_command(dev, CommandVals.Pattern, [PatternVals.DisplayPanic]),
14
+ "\"LOTUS\" Top Down": lambda dev: send_command(dev, CommandVals.Pattern, [PatternVals.DisplayLotus2]),
15
+ "All brightness levels (1 LED each)": lambda dev: all_brightnesses(dev),
16
+ "Every Second Row": lambda dev: every_nth_row(dev, 2),
17
+ "Every Third Row": lambda dev: every_nth_row(dev, 3),
18
+ "Every Fourth Row": lambda dev: every_nth_row(dev, 4),
19
+ "Every Fifth Row": lambda dev: every_nth_row(dev, 5),
20
+ "Every Sixth Row": lambda dev: every_nth_row(dev, 6),
21
+ "Every Second Col": lambda dev: every_nth_col(dev, 2),
22
+ "Every Third Col": lambda dev: every_nth_col(dev, 3),
23
+ "Every Fourth Col": lambda dev: every_nth_col(dev, 4),
24
+ "Every Fifth Col": lambda dev: every_nth_col(dev, 5),
25
+ "Checkerboard": lambda dev: checkerboard(dev, 1),
26
+ "Double Checkerboard": lambda dev: checkerboard(dev, 2),
27
+ "Triple Checkerboard": lambda dev: checkerboard(dev, 3),
28
+ "Quad Checkerboard": lambda dev: checkerboard(dev, 4),
29
+ }
@@ -0,0 +1,70 @@
1
+ """
2
+
3
+
4
+ Author:
5
+ Inspyre Softworks
6
+
7
+ Project:
8
+ led-matrix-battery
9
+
10
+ File:
11
+ ${DIR_PATH}/${FILE_NAME}
12
+
13
+
14
+ Description:
15
+ $DESCRIPTION
16
+
17
+ """
18
+ import serial
19
+
20
+ from is_matrix_forge.led_matrix.constants import WIDTH, HEIGHT
21
+ from is_matrix_forge.led_matrix.display.helpers.columns import send_col, commit_cols
22
+
23
+
24
+ def all_brightnesses(dev):
25
+ """Increase the brightness with each pixel.
26
+ Only 0-255 available, so it can't fill all 306 LEDs"""
27
+ with serial.Serial(dev.device, 115200) as s:
28
+ for x in range(0, WIDTH):
29
+ vals = [0 for _ in range(HEIGHT)]
30
+
31
+ for y in range(HEIGHT):
32
+ brightness = x + WIDTH * y
33
+ if brightness > 255:
34
+ vals[y] = 0
35
+ else:
36
+ vals[y] = brightness
37
+
38
+ send_col(dev, s, x, vals)
39
+ commit_cols(dev, s)
40
+
41
+
42
+
43
+ def every_nth_row(dev, n):
44
+ with serial.Serial(dev.device, 115200) as s:
45
+ for x in range(0, WIDTH):
46
+ vals = [(0xFF if y % n == 0 else 0) for y in range(HEIGHT)]
47
+
48
+ send_col(dev, s, x, vals)
49
+ commit_cols(dev, s)
50
+
51
+
52
+ def every_nth_col(dev, n):
53
+ with serial.Serial(dev.device, 115200) as s:
54
+ for x in range(0, WIDTH):
55
+ vals = [(0xFF if x % n == 0 else 0) for _ in range(HEIGHT)]
56
+
57
+ send_col(dev, s, x, vals)
58
+ commit_cols(dev, s)
59
+
60
+
61
+ def checkerboard(dev, n):
62
+ with serial.Serial(dev.device, 115200) as s:
63
+ for x in range(0, WIDTH):
64
+ vals = (([0xFF] * n) + ([0x00] * n)) * int(HEIGHT / 2)
65
+ if x % (n * 2) < n:
66
+ # Rotate once
67
+ vals = vals[n:] + vals[:n]
68
+
69
+ send_col(dev, s, x, vals)
70
+ commit_cols(dev, s)
@@ -0,0 +1,2 @@
1
+ import serial
2
+ from is_matrix_forge.led_matrix.constants import HEIGHT, WIDTH
@@ -0,0 +1,66 @@
1
+ from .scroller import TextScroller
2
+ """
3
+ Text and symbol rendering module for LED Matrix.
4
+
5
+ This module provides functions for displaying text and symbols on the LED matrix.
6
+ It includes functions for rendering strings, fonts, and special symbols.
7
+ """
8
+
9
+ from is_matrix_forge.led_matrix import fonts as font
10
+ from is_matrix_forge.led_matrix.commands import send_command
11
+ from ...commands.map import CommandVals
12
+
13
+
14
+ def show_string(dev, s):
15
+ """Render a string with up to five letters"""
16
+ show_font(dev, [font.convert_font(letter) for letter in str(s)[:5]])
17
+
18
+
19
+ def show_font(dev, font_items):
20
+ """Render up to five 5x6 pixel font items"""
21
+ # Initialize a byte array with all pixels off
22
+ vals = [0x00 for _ in range(39)]
23
+
24
+ # Process each font item (character/symbol)
25
+ for digit_i, digit_pixels in enumerate(font_items):
26
+ # Calculate vertical offset for this character
27
+ # Each character is placed 7 pixels apart vertically
28
+ offset = digit_i * 7
29
+
30
+ # Process each pixel in the 5x6 character grid
31
+ for pixel_x in range(5):
32
+ for pixel_y in range(6):
33
+ # Convert 2D coordinates to 1D index in the font data
34
+ # Font data is stored as a 1D array in row-major order
35
+ pixel_value = digit_pixels[pixel_x + pixel_y * 5]
36
+
37
+ # Calculate the position in the LED matrix
38
+ # Characters start at x=2 (to center them) and are stacked vertically
39
+ # with the calculated offset
40
+ i = (2 + pixel_x) + (9 * (pixel_y + offset))
41
+
42
+ # If this pixel should be on
43
+ if pixel_value:
44
+ # Calculate which byte and bit to set
45
+ byte_index = int(i / 8)
46
+ bit_position = i % 8
47
+
48
+ # Set the bit in the appropriate byte
49
+ vals[byte_index] = vals[byte_index] | (1 << bit_position)
50
+
51
+ # Send the command to display the characters
52
+ send_command(dev, CommandVals.Draw, vals)
53
+
54
+
55
+ def show_symbols(dev, symbols):
56
+ """Render a list of up to five symbols
57
+ Can use letters/numbers or symbol names, like 'sun', ':)'"""
58
+ font_items = []
59
+ for symbol in symbols:
60
+ s = font.convert_symbol(symbol)
61
+ if not s:
62
+ s = font.convert_font(symbol)
63
+ font_items.append(s)
64
+
65
+ show_font(dev, font_items)
66
+
@@ -0,0 +1,136 @@
1
+ """
2
+
3
+
4
+ Author:
5
+ Inspyre Softworks
6
+
7
+ Project:
8
+ led-matrix-battery
9
+
10
+ File:
11
+ ${DIR_PATH}/${FILE_NAME}
12
+
13
+
14
+ Description:
15
+ $DESCRIPTION
16
+
17
+ """
18
+ from typing import List
19
+
20
+
21
+
22
+ class TextScroller:
23
+ """
24
+ Generate Frame objects for scrolling text bottom-to-top on an LED matrix.
25
+
26
+ Frames are returned as a list of "columns", each column is a list of bits down the rows.
27
+ """
28
+
29
+ def __init__(
30
+ self,
31
+ font,
32
+ rows: int = 34,
33
+ cols: int = 9,
34
+ delay: float = 0.1,
35
+ letter_width: int = 5,
36
+ letter_height: int = 6,
37
+ spacing: int = 1,
38
+ col_offset: int = 2,
39
+ ):
40
+ """
41
+ Initialize a TextScroller.
42
+
43
+ Parameters:
44
+ font:
45
+ A font object with convert_font(char) -> glyph callable.
46
+ rows:
47
+ Number of rows in the matrix.
48
+ cols:
49
+ Number of columns in the matrix.
50
+ delay:
51
+ Duration for each generated Frame.
52
+ letter_width:
53
+ Width of each character in pixels.
54
+ letter_height:
55
+ Height of each character in pixels.
56
+ spacing:
57
+ Vertical spacing between characters.
58
+ col_offset:
59
+ Horizontal offset for character placement.
60
+ """
61
+ self.font = font
62
+ self.rows = rows
63
+ self.cols = cols
64
+ self.delay = delay
65
+ self.letter_width = letter_width
66
+ self.letter_height = letter_height
67
+ self.spacing = spacing
68
+ self.col_offset = col_offset
69
+
70
+ def scroll(self, text: str, skip_end_space: bool = False) -> List['Frame']:
71
+ """
72
+ Build and return a list of Frame objects representing the text scroll.
73
+
74
+ Parameters:
75
+ text:
76
+ The text string to scroll.
77
+ skip_end_space:
78
+ If True, omit trailing blank frames after text.
79
+
80
+ Returns:
81
+ A list of Frame instances, each covering the full matrix as columns×rows.
82
+ """
83
+ from is_matrix_forge.led_matrix.display.animations.frame import Frame
84
+
85
+ grid = self._build_big_grid(text)
86
+ total_height = len(grid)
87
+ end_limit = total_height if skip_end_space else total_height + self.rows
88
+ frames: List[Frame] = []
89
+
90
+ for top in range(-self.rows, end_limit):
91
+ # extract a rows×cols slice (list of rows)
92
+ slice_rows = [
93
+ grid[r] if 0 <= r < total_height else [0] * self.cols
94
+ for r in range(top, top + self.rows)
95
+ ]
96
+ # reorient to columns×rows
97
+ cols_grid = self._orient_frame(slice_rows)
98
+ frames.append(Frame(cols_grid, duration=self.delay))
99
+
100
+ return frames
101
+
102
+ def _build_big_grid(self, text: str) -> List[List[int]]:
103
+ """
104
+ Build a concatenated vertical grid of all glyphs in text as list of rows.
105
+
106
+ Returns:
107
+ A 2D list of bits [total_height][cols].
108
+ """
109
+ font_items = [self.font.convert_font(c) for c in text]
110
+ total_height = len(font_items) * (self.letter_height + self.spacing) - self.spacing
111
+ grid = [[0] * self.cols for _ in range(total_height)]
112
+
113
+ for idx, glyph in enumerate(font_items):
114
+ base = idx * (self.letter_height + self.spacing)
115
+ for x in range(self.letter_width):
116
+ for y in range(self.letter_height):
117
+ if glyph[x + y * self.letter_width]:
118
+ grid[base + y][self.col_offset + x] = 1
119
+
120
+ return grid
121
+
122
+ def _orient_frame(self, rows_grid: List[List[int]]) -> List[List[int]]:
123
+ """
124
+ Transpose a rows×cols grid into cols×rows orientation.
125
+
126
+ Parameters:
127
+ rows_grid:
128
+ A list of self.rows lists, each length self.cols.
129
+
130
+ Returns:
131
+ A list of self.cols lists, each length self.rows.
132
+ """
133
+ return [
134
+ [rows_grid[r][c] for r in range(self.rows)]
135
+ for c in range(self.cols)
136
+ ]
@@ -0,0 +1,4 @@
1
+ from .matrix import *
2
+ from .grid import *
3
+
4
+
@@ -0,0 +1,14 @@
1
+ from inspyre_toolbox.exceptional import CustomRootException
2
+
3
+
4
+ class LEDMatrixControllerError(CustomRootException):
5
+ """
6
+ Base class for all LED matrix controller errors.
7
+
8
+ Class Attributes:
9
+ default_message (str): Read-Only. The default message for the error.
10
+ """
11
+ default_message = 'An error occurred with the LED matrix controller library!'
12
+
13
+
14
+ __all__ = ['LEDMatrixControllerError']