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,119 @@
1
+ import json
2
+ from pathlib import Path
3
+ from typing import Dict, List, Optional, Union
4
+ from inspyre_toolbox.syntactic_sweets.classes.decorators import validate_type
5
+
6
+
7
+ class FontMap:
8
+ """
9
+ FontMap allows lookup of character and symbol patterns from a JSON-based font map.
10
+ """
11
+ DEFAULT_FALLBACK_CHAR = '?'
12
+
13
+ def __init__(
14
+ self,
15
+ font_map: Optional[Dict[str, Dict[str, List[int]]]] = None,
16
+ characters_key: str = 'characters',
17
+ symbols_key: str = 'symbols',
18
+ case_sensitive: bool = False,
19
+ fallback_char: Optional[str] = None
20
+ ):
21
+ """
22
+ Initialize a FontMap instance.
23
+
24
+ Parameters:
25
+ font_map: Raw font map dict loaded from JSON (or None to load from JSON file).
26
+ characters_key: Top-level key for character definitions.
27
+ symbols_key: Top-level key for symbol definitions.
28
+ case_sensitive: Toggle case-sensitive lookup.
29
+ fallback_char: Character to use when lookup fails.
30
+ """
31
+ # Lazy-load from JSON if not provided
32
+ if font_map is None:
33
+ map_path = Path(__file__).parent / 'font_map.json'
34
+ font_map = json.load(open(map_path))
35
+
36
+ self._map = font_map
37
+ self._characters_key = characters_key
38
+ self._symbols_key = symbols_key
39
+ self._case_sensitive = case_sensitive
40
+ self._fallback_char = fallback_char or self.DEFAULT_FALLBACK_CHAR
41
+
42
+ @property
43
+ def character_map(self) -> Dict[str, List[int]]:
44
+ return self._map[self._characters_key]
45
+
46
+ @property
47
+ def symbol_map(self) -> Dict[str, List[int]]:
48
+ return self._map[self._symbols_key]
49
+
50
+ @property
51
+ def characters(self) -> List[str]:
52
+ return list(self.character_map.keys())
53
+
54
+ @property
55
+ def symbols(self) -> List[str]:
56
+ return list(self.symbol_map.keys())
57
+
58
+ @property
59
+ def fallback_char(self) -> str:
60
+ return self._fallback_char
61
+
62
+ @fallback_char.setter
63
+ def fallback_char(self, new: str):
64
+ if not isinstance(new, str) or len(new) != 1:
65
+ raise ValueError('fallback_char must be a single character string')
66
+ if new not in self.characters + self.symbols:
67
+ raise ValueError(f'{new!r} not found in font map')
68
+ self._fallback_char = new
69
+
70
+ @property
71
+ def is_case_sensitive(self) -> bool:
72
+ return self._case_sensitive
73
+
74
+ @validate_type(bool)
75
+ @is_case_sensitive.setter
76
+ def is_case_sensitive(self, new: bool):
77
+ self._case_sensitive = new
78
+
79
+ def __contains__(self, key: str) -> bool:
80
+ key_norm = self._normalize_key(key)
81
+ return key_norm in self.character_map or key_norm in self.symbol_map
82
+
83
+ def __repr__(self):
84
+ return (
85
+ f"{self.__class__.__name__}(case_sensitive={self._case_sensitive}, "
86
+ f"fallback_char={self._fallback_char!r})"
87
+ )
88
+
89
+ def lookup(self, key: str, kind: Optional[str] = None) -> List[int]:
90
+ """
91
+ Lookup a character or symbol. If kind is 'character' or 'symbol', force that map.
92
+ """
93
+ key_norm = self._normalize_key(key)
94
+ if kind == 'symbol':
95
+ return self._lookup_map(self.symbol_map, key_norm)
96
+ if kind in ('char', 'character'):
97
+ return self._lookup_map(self.character_map, key_norm)
98
+
99
+ # Try characters first, then symbols
100
+ try:
101
+ return self._lookup_map(self.character_map, key_norm)
102
+ except KeyError:
103
+ return self._lookup_map(self.symbol_map, key_norm)
104
+
105
+ def reload(self, font_map: Union[str, Path, Dict]):
106
+ """
107
+ Reloads font map from a file path or dict.
108
+ """
109
+ if isinstance(font_map, (str, Path)):
110
+ font_map = json.load(open(font_map))
111
+ self._map = font_map
112
+
113
+ def _normalize_key(self, key: str) -> str:
114
+ return key if self._case_sensitive else key.upper()
115
+
116
+ def _lookup_map(self, mapping: Dict[str, List[int]], key: str) -> List[int]:
117
+ if key not in mapping:
118
+ key = self._fallback_char
119
+ return mapping[key]
File without changes
@@ -0,0 +1,152 @@
1
+ import functools
2
+ from typing import Optional, Type, Callable, Any
3
+
4
+ import functools
5
+ from typing import Optional, Type, Callable, Any
6
+ from inspyre_toolbox.syntactic_sweets.classes.decorators import validate_type
7
+ from is_matrix_forge.led_matrix.errors.misc import ImplicitNameDerivationError
8
+
9
+
10
+ def freeze_setter(
11
+ attr_name: Optional[str] = None,
12
+ exception: Type[Exception] = AttributeError,
13
+ allow_none_reassignment: bool = False,
14
+ treat_none_as_unset: bool = True,
15
+ allow_reassignment_to_same_value: bool = False,
16
+ fail_silently: bool = False
17
+ ):
18
+ """
19
+ Function decorator for a @<prop>.setter that raises if you try to set again.
20
+
21
+ This decorator prevents reassigning a property after it's been set once,
22
+ with configurable exceptions.
23
+
24
+ Parameters:
25
+ attr_name (str, optional):
26
+ The name of the underlying private attribute to check. If not provided,
27
+ it will be derived from the setter's function name by prepending a
28
+ single underscore (e.g., `value` becomes `_value`). If your setter
29
+ name starts with a single underscore (e.g., `_internal_val`), you *must*
30
+ provide this parameter explicitly.
31
+ exception (Exception class, optional):
32
+ The exception to raise on reassignment.
33
+ Defaults to raise an AttributeError.
34
+ allow_none_reassignment (bool, optional):
35
+ If True, allows setting the property to None even if it's already set.
36
+ Defaults to False.
37
+ treat_none_as_unset (bool, optional):
38
+ If True, None values are considered as unset, allowing the property
39
+ to be set if its current value is None.
40
+ Defaults to True.
41
+ allow_reassignment_to_same_value (bool, optional):
42
+ If True, allows reassigning the property with the same value.
43
+ It passes silently and does not call the setter again.
44
+ Defaults to False.
45
+ fail_silently (bool, optional):
46
+ If True, instead of raising an exception on a forbidden reassignment,
47
+ the setter simply returns silently.
48
+ Defaults to False.
49
+
50
+ Raises:
51
+ ImplicitNameDerivationError:
52
+ If the decorated setter's name starts with a single underscore
53
+ (but not a double underscore, which has its own meaning) and
54
+ `attr_name` is not provided. This is to prevent accidental
55
+ triggering of Python's name mangling.
56
+ exception: (Defaults to AttributeError)
57
+ Raised when a forbidden attempt to reassign the property occurs,
58
+ unless `fail_silently` is True.
59
+
60
+ Example:
61
+ ```python
62
+ class MyClass:
63
+ def __init__(self):
64
+ self._value = None # Underlying attribute
65
+
66
+ @property
67
+ def value(self):
68
+ return self._value
69
+
70
+ @value.setter
71
+ @freeze_setter() # Derives attr_name as '_value'
72
+ def value(self, new_value):
73
+ self._value = new_value
74
+
75
+ obj = MyClass()
76
+ obj.value = 42
77
+ # obj.value = 100 # Raises AttributeError
78
+
79
+ class InternalPropClass:
80
+ def __init__(self):
81
+ self.__internal_storage = None # Explicit private attribute
82
+
83
+ @property
84
+ def _internal_prop(self):
85
+ return self.__internal_storage
86
+
87
+ @_internal_prop.setter
88
+ @freeze_setter(attr_name='__internal_storage') # Must provide attr_name
89
+ def _internal_prop(self, new_value):
90
+ self.__internal_storage = new_value
91
+ ```
92
+
93
+ See Also:
94
+ - `ImplicitNameDerivationError`: Raised for configuration issues with underscore-prefixed setters.
95
+ - `AttributeError`: Default exception for reassigning a frozen attribute.
96
+ """
97
+ def decorator(setter: Callable) -> Callable:
98
+ setter_name = setter.__name__
99
+
100
+ # Check if the setter name starts with a single '_' and attr_name isn't provided.
101
+ # We allow double underscores ('__') as they have special meaning and are less likely
102
+ # to be used for regular property setters that would rely on implicit derivation.
103
+ if setter_name.startswith('_') and not setter_name.startswith('__') and attr_name is None:
104
+ raise ImplicitNameDerivationError() # Raise error if no explicit attr_name is given for such setters.
105
+
106
+ private_attr_name = attr_name or f'_{setter_name}'
107
+
108
+ @functools.wraps(setter)
109
+ def wrapper(self, new_value: Any) -> Any:
110
+ current_value = getattr(self, private_attr_name, None)
111
+
112
+ # Determine if the attribute is considered 'set'
113
+ # An attribute is considered "set" if:
114
+ # 1. treat_none_as_unset is False (meaning None is a valid set value)
115
+ # OR
116
+ # 2. treat_none_as_unset is True AND current_value is not None
117
+ is_set = not treat_none_as_unset or current_value is not None
118
+
119
+ if is_set:
120
+ # Case 1: Reassigning to the same value is allowed and current value matches new value
121
+ if allow_reassignment_to_same_value and new_value == current_value:
122
+ return # Pass silently without calling setter or raising
123
+
124
+ # Case 2: Reassigning to None is allowed and new value is None
125
+ # This only applies if the property is already considered "set".
126
+ # If treat_none_as_unset is True, setting to None effectively "unsets" it.
127
+ if allow_none_reassignment and new_value is None:
128
+ return setter(self, new_value) # Allow and call setter
129
+
130
+ # Case 3: Forbidden reassignment
131
+ if fail_silently:
132
+ return # Fail silently
133
+
134
+ raise exception(
135
+ f"Cannot reassign {self.__class__.__name__}.{setter_name} "
136
+ f"(current value: {current_value!r}, private attribute: '{private_attr_name}')"
137
+ )
138
+
139
+ # If the attribute wasn't considered set, or if it's being set to None
140
+ # when treat_none_as_unset is True (which is like an initial set), call the setter.
141
+ return setter(self, new_value)
142
+
143
+ return wrapper
144
+
145
+ return decorator
146
+
147
+
148
+ __all__ = [
149
+ 'ImplicitNameDerivationError',
150
+ 'freeze_setter',
151
+ 'validate_type',
152
+ ]
@@ -0,0 +1,20 @@
1
+ """
2
+ Directory configuration for the LED Matrix Battery application.
3
+
4
+ This module provides platform-specific directory paths for storing application
5
+ data, configuration files, and other resources using the platformdirs library.
6
+ """
7
+
8
+ from platformdirs import PlatformDirs
9
+
10
+
11
+ APP_DIRS = PlatformDirs('LEDMatrixLib', appauthor='Inspyre Softworks')
12
+ APP_DIR = APP_DIRS.user_data_path
13
+ PRESETS_DIR = APP_DIR.joinpath('presets')
14
+
15
+
16
+ __all__ = [
17
+ 'APP_DIRS',
18
+ 'APP_DIR',
19
+ 'PRESETS_DIR',
20
+ ]
@@ -0,0 +1,132 @@
1
+ import hashlib
2
+ from pathlib import Path
3
+ from typing import Optional
4
+ import requests
5
+ from inspyre_toolbox.path_man import provision_path
6
+
7
+
8
+ def download_presets():
9
+ from is_matrix_forge.led_matrix.constants import PROJECT_URLS
10
+
11
+ url = PROJECT_URLS['github_api']
12
+
13
+
14
+
15
+ def calculate_checksum(file_path):
16
+ """
17
+ Calculate the SHA-256 checksum of a file.
18
+
19
+ Parameters:
20
+ file_path (Union[str, Path]):
21
+ The path to the file for which to calculate the checksum.
22
+
23
+ Returns:
24
+ str:
25
+ The SHA-256 checksum of the file.
26
+ """
27
+ if not isinstance(file_path, Path):
28
+ file_path = provision_path(file_path)
29
+
30
+ sha256 = hashlib.sha256()
31
+
32
+ with open(file_path, 'rb') as f:
33
+ for chunk in iter(lambda: f.read(4096), b''):
34
+ sha256.update(chunk)
35
+
36
+ return sha256.hexdigest()
37
+
38
+
39
+ def coerce_to_int(value) -> Optional[int]:
40
+ """
41
+ Attempt to coerce a value to an integer.
42
+
43
+ Parameters:
44
+ value (Any):
45
+ The value to coerce.
46
+
47
+ Returns:
48
+ Optional[int]:
49
+ The coerced integer value, or None if the coercion fails.
50
+ """
51
+ try:
52
+ return int(value)
53
+ except (ValueError, TypeError):
54
+ return None
55
+
56
+
57
+ def percentage_to_value(percent, max_value=255):
58
+ """
59
+ Convert a percentage value to an actual value between 0 and `max_value`.
60
+
61
+ Parameters:
62
+ percent (float):
63
+ The percentage value to convert.
64
+
65
+ max_value (int, optional):
66
+ The maximum value to which the percentage should be converted. Defaults to 255.
67
+
68
+ Returns:
69
+ int:
70
+ The converted value between 0 and `max_value`.
71
+ """
72
+ if not isinstance(percent, (int, float)):
73
+ raise TypeError(f'percent must be of type int or float, not {type(percent)}')
74
+
75
+ if percent < 0 or percent > 100:
76
+ raise ValueError(f'percent must be between 0 and 100, not {percent}')
77
+
78
+ return int(round(percent * max_value / 100))
79
+
80
+
81
+ def verify_checksum(remote_checksum, local_file_path):
82
+ """
83
+ Verify the checksum of a local file against a remote checksum.
84
+
85
+ Parameters:
86
+ remote_checksum (str):
87
+ The remote checksum to verify against.
88
+
89
+ local_file_path (Union[str, Path]):
90
+ The path to the local file to verify.
91
+
92
+ Returns:
93
+ bool:
94
+ True if the checksums match, False otherwise.
95
+ """
96
+ if not isinstance(local_file_path, Path):
97
+ local_file_path = provision_path(local_file_path)
98
+
99
+ sha256 = hashlib.sha256()
100
+
101
+ with open(local_file_path, 'rb') as f:
102
+ for chunk in iter(lambda: f.read(4096), b''):
103
+ sha256.update(chunk)
104
+
105
+ local_checksum = sha256.hexdigest()
106
+
107
+ return local_checksum == remote_checksum
108
+
109
+
110
+ def download_file(url, local_path, headers=None):
111
+ """
112
+ Download a file from a URL and save it to a local path.
113
+
114
+ Parameters:
115
+ url (str):
116
+ The URL of the file to download.
117
+
118
+ local_path (Union[str, Path]):
119
+ The local path to save the downloaded file.
120
+
121
+ headers (Optional[Dict]):
122
+ The headers to use for the request. Defaults to None.
123
+ """
124
+ if not isinstance(local_path, Path):
125
+ local_path = provision_path(local_path)
126
+
127
+ response = requests.get(url, headers=headers, stream=True)
128
+ response.raise_for_status()
129
+
130
+ with open(local_path, 'wb') as f:
131
+ for chunk in response.iter_content(chunk_size=8192):
132
+ f.write(chunk)
@@ -0,0 +1,67 @@
1
+ """
2
+
3
+
4
+ Author:
5
+ Inspyre Softworks
6
+
7
+ Project:
8
+ led-matrix-battery
9
+
10
+ File:
11
+ is_matrix_forge/common/helpers/github_api.py
12
+
13
+
14
+ Description:
15
+
16
+
17
+ """
18
+ from typing import Optional
19
+ from is_matrix_forge.led_matrix.constants import PROJECT_URLS
20
+
21
+ GITHUB_URL_SUFFIX = '?ref=main'
22
+
23
+
24
+ def assemble_github_url_suffix(branch: Optional[str] = None):
25
+ """
26
+ Assemble the URL suffix for the GitHub link.
27
+
28
+ Parameters:
29
+ branch (Optional[str]):
30
+ The name of the target GitHub repo branch.
31
+ Optional, defaults to "main".
32
+
33
+ Returns:
34
+ str:
35
+ The GitHub URL suffix. Specifically, a reference tag to a target branch.
36
+ """
37
+ return f'?ref={branch or "main"}'
38
+
39
+
40
+ def assemble_github_content_path_url(
41
+ relative_path: Optional[str] = 'presets', # e.g. 'dev_tools/presets.py'
42
+ branch: Optional[str] = 'master',
43
+ base_url: Optional[str] = PROJECT_URLS['github_api'],
44
+ suffix: Optional[str] = None
45
+ ):
46
+ """
47
+ Assemble a GitHub content path URL.
48
+
49
+ Parameters:
50
+ relative_path (Optional[str]):
51
+ The relative path to the file in the GitHub repository.
52
+ branch (Optional[str]):
53
+ The branch name. Defaults to 'master'.
54
+ base_url (Optional[str]):
55
+ The base URL of the GitHub API. Defaults to PROJECT_URLS['github_api'].
56
+ suffix (Optional[str]):
57
+ The URL suffix. Defaults to GITHUB_URL_SUFFIX.
58
+
59
+ Returns:
60
+ str:
61
+ The assembled GitHub content path URL.
62
+ """
63
+ return f"{base_url}/{relative_path}{suffix or assemble_github_url_suffix(branch)}"
64
+
65
+
66
+ REPO_PRESETS_URL = assemble_github_content_path_url()
67
+
@@ -0,0 +1,8 @@
1
+ """
2
+ Author: Taylor B. tayjaybabee@gmail.com
3
+ Date: 2025-04-30 16:01:10
4
+ LastEditors: Taylor B. tayjaybabee@gmail.com
5
+ LastEditTime: 2025-04-30 16:01:49
6
+ FilePath: is_matrix_forge/designer_gui/__init__.py
7
+ Description: 这是默认设置,可以在设置》工具》File Description中进行配置
8
+ """