bluer-sbc 8.3.1__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.

Potentially problematic release.


This version of bluer-sbc might be problematic. Click here for more details.

Files changed (82) hide show
  1. bluer_sbc/.abcli/abcli.sh +12 -0
  2. bluer_sbc/.abcli/actions.sh +11 -0
  3. bluer_sbc/.abcli/adafruit_rgb_matrix.sh +14 -0
  4. bluer_sbc/.abcli/alias.sh +5 -0
  5. bluer_sbc/.abcli/blue_sbc.sh +11 -0
  6. bluer_sbc/.abcli/camera.sh +20 -0
  7. bluer_sbc/.abcli/grove.sh +43 -0
  8. bluer_sbc/.abcli/hat.sh +22 -0
  9. bluer_sbc/.abcli/install/adafruit_rgb_matrix.sh +15 -0
  10. bluer_sbc/.abcli/install/grove.sh +29 -0
  11. bluer_sbc/.abcli/install/lepton.sh +33 -0
  12. bluer_sbc/.abcli/install/rpi.sh +65 -0
  13. bluer_sbc/.abcli/install/scroll_phat_hd.sh +14 -0
  14. bluer_sbc/.abcli/install/sparkfun_top_phat.sh +33 -0
  15. bluer_sbc/.abcli/install/template.sh +9 -0
  16. bluer_sbc/.abcli/install/unicorn_16x16.sh +16 -0
  17. bluer_sbc/.abcli/lepton.sh +15 -0
  18. bluer_sbc/.abcli/scroll_phat_hd.sh +14 -0
  19. bluer_sbc/.abcli/session.sh +39 -0
  20. bluer_sbc/.abcli/sparkfun_top_phat.sh +27 -0
  21. bluer_sbc/.abcli/tests/README.sh +8 -0
  22. bluer_sbc/.abcli/tests/camera.sh +47 -0
  23. bluer_sbc/.abcli/tests/help.sh +65 -0
  24. bluer_sbc/.abcli/tests/version.sh +8 -0
  25. bluer_sbc/.abcli/unicorn_16x16.sh +14 -0
  26. bluer_sbc/README.py +51 -0
  27. bluer_sbc/__init__.py +17 -0
  28. bluer_sbc/__main__.py +16 -0
  29. bluer_sbc/algo/__init__.py +0 -0
  30. bluer_sbc/algo/diff.py +81 -0
  31. bluer_sbc/config.env +30 -0
  32. bluer_sbc/env.py +35 -0
  33. bluer_sbc/hardware/__init__.py +38 -0
  34. bluer_sbc/hardware/adafruit_rgb_matrix.py +30 -0
  35. bluer_sbc/hardware/display.py +112 -0
  36. bluer_sbc/hardware/grove.py +104 -0
  37. bluer_sbc/hardware/hardware.py +58 -0
  38. bluer_sbc/hardware/hat/__init__.py +0 -0
  39. bluer_sbc/hardware/hat/__main__.py +91 -0
  40. bluer_sbc/hardware/hat/abstract.py +136 -0
  41. bluer_sbc/hardware/hat/prototype.py +161 -0
  42. bluer_sbc/hardware/screen.py +17 -0
  43. bluer_sbc/hardware/scroll_phat_hd.py +35 -0
  44. bluer_sbc/hardware/sparkfun_top_phat/__init__.py +0 -0
  45. bluer_sbc/hardware/sparkfun_top_phat/__main__.py +51 -0
  46. bluer_sbc/hardware/sparkfun_top_phat/classes.py +104 -0
  47. bluer_sbc/hardware/unicorn_16x16.py +44 -0
  48. bluer_sbc/help/__init__.py +0 -0
  49. bluer_sbc/help/__main__.py +10 -0
  50. bluer_sbc/help/adafruit_rgb_matrix.py +23 -0
  51. bluer_sbc/help/camera.py +71 -0
  52. bluer_sbc/help/functions.py +52 -0
  53. bluer_sbc/help/grove.py +59 -0
  54. bluer_sbc/help/hat.py +56 -0
  55. bluer_sbc/help/lepton.py +39 -0
  56. bluer_sbc/help/scroll_phat_hd.py +23 -0
  57. bluer_sbc/help/session.py +26 -0
  58. bluer_sbc/help/sparkfun_top_phat.py +26 -0
  59. bluer_sbc/help/unicorn_16x16.py +23 -0
  60. bluer_sbc/host.py +11 -0
  61. bluer_sbc/imager/__init__.py +16 -0
  62. bluer_sbc/imager/camera/__init__.py +3 -0
  63. bluer_sbc/imager/camera/__main__.py +69 -0
  64. bluer_sbc/imager/camera/classes.py +259 -0
  65. bluer_sbc/imager/camera/constants.py +30 -0
  66. bluer_sbc/imager/classes.py +25 -0
  67. bluer_sbc/imager/lepton/__init__.py +3 -0
  68. bluer_sbc/imager/lepton/__main__.py +51 -0
  69. bluer_sbc/imager/lepton/classes.py +35 -0
  70. bluer_sbc/imager/lepton/python2.py +70 -0
  71. bluer_sbc/logger.py +5 -0
  72. bluer_sbc/sample.env +1 -0
  73. bluer_sbc/session/__init__.py +0 -0
  74. bluer_sbc/session/__main__.py +27 -0
  75. bluer_sbc/session/classes.py +318 -0
  76. bluer_sbc/session/functions.py +22 -0
  77. bluer_sbc/urls.py +1 -0
  78. bluer_sbc-8.3.1.dist-info/METADATA +58 -0
  79. bluer_sbc-8.3.1.dist-info/RECORD +82 -0
  80. bluer_sbc-8.3.1.dist-info/WHEEL +5 -0
  81. bluer_sbc-8.3.1.dist-info/licenses/LICENSE +121 -0
  82. bluer_sbc-8.3.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,52 @@
1
+ from typing import List
2
+
3
+ from bluer_options.terminal import show_usage
4
+ from bluer_ai.help.generic import help_functions as generic_help_functions
5
+
6
+ from bluer_sbc.help.adafruit_rgb_matrix import (
7
+ help_functions as help_adafruit_rgb_matrix,
8
+ )
9
+ from bluer_sbc.help.camera import help_functions as help_camera
10
+ from bluer_sbc.help.grove import help_functions as help_grove
11
+ from bluer_sbc.help.hat import help_functions as help_hat
12
+ from bluer_sbc.help.lepton import help_functions as help_lepton
13
+ from bluer_sbc.help.scroll_phat_hd import help_functions as help_scroll_phat_hd
14
+ from bluer_sbc.help.sparkfun_top_phat import help_functions as help_sparkfun_top_phat
15
+ from bluer_sbc.help.session import help_functions as help_session
16
+ from bluer_sbc.help.unicorn_16x16 import help_functions as help_unicorn_16x16
17
+ from bluer_sbc import ALIAS
18
+
19
+
20
+ def help_browse(
21
+ tokens: List[str],
22
+ mono: bool,
23
+ ) -> str:
24
+ options = "actions|repo"
25
+
26
+ return show_usage(
27
+ [
28
+ "@plugin",
29
+ "browse",
30
+ f"[{options}]",
31
+ ],
32
+ "browse bluer_sbc.",
33
+ mono=mono,
34
+ )
35
+
36
+
37
+ help_functions = generic_help_functions(plugin_name=ALIAS)
38
+
39
+ help_functions.update(
40
+ {
41
+ "adafruit_rgb_matrix": help_adafruit_rgb_matrix,
42
+ "browse": help_browse,
43
+ "camera": help_camera,
44
+ "grove": help_grove,
45
+ "hat": help_hat,
46
+ "lepton": help_lepton,
47
+ "scroll_phat_hd": help_scroll_phat_hd,
48
+ "session": help_session,
49
+ "sparkfun_top_phat": help_sparkfun_top_phat,
50
+ "unicorn_16x16": help_unicorn_16x16,
51
+ }
52
+ )
@@ -0,0 +1,59 @@
1
+ from typing import List
2
+
3
+ from blue_options.terminal import show_usage
4
+
5
+
6
+ def help_info(
7
+ tokens: List[str],
8
+ mono: bool,
9
+ ) -> str:
10
+ return show_usage(
11
+ [
12
+ "grove",
13
+ "info",
14
+ ],
15
+ "show grove info.",
16
+ mono=mono,
17
+ )
18
+
19
+
20
+ def help_validate(
21
+ tokens: List[str],
22
+ mono: bool,
23
+ ) -> str:
24
+ options = "adc | button"
25
+
26
+ return show_usage(
27
+ [
28
+ "grove",
29
+ "validate",
30
+ f"[{options}]",
31
+ ],
32
+ "validate grove.",
33
+ mono=mono,
34
+ )
35
+
36
+
37
+ def help_validate_oled_128x64(
38
+ tokens: List[str],
39
+ mono: bool,
40
+ ) -> str:
41
+ options = "animate | buttons | image | shapes | stats"
42
+
43
+ return show_usage(
44
+ [
45
+ "grove",
46
+ "validate",
47
+ "oled_128x64",
48
+ f"[{options}]",
49
+ ],
50
+ "validate grove oled_128x64.",
51
+ mono=mono,
52
+ )
53
+
54
+
55
+ help_functions = {
56
+ "info": help_info,
57
+ "validate": help_validate,
58
+ "validate_oled_128x64": help_validate_oled_128x64,
59
+ }
bluer_sbc/help/hat.py ADDED
@@ -0,0 +1,56 @@
1
+ from typing import List
2
+
3
+ from blue_options.terminal import show_usage
4
+
5
+
6
+ def help_input(
7
+ tokens: List[str],
8
+ mono: bool,
9
+ ) -> str:
10
+ return show_usage(
11
+ [
12
+ "@sbc",
13
+ "hat",
14
+ "input",
15
+ ],
16
+ "read hat inputs.",
17
+ mono=mono,
18
+ )
19
+
20
+
21
+ def help_output(
22
+ tokens: List[str],
23
+ mono: bool,
24
+ ) -> str:
25
+ return show_usage(
26
+ [
27
+ "@sbc",
28
+ "hat",
29
+ "output",
30
+ "<10101010>",
31
+ ],
32
+ "activate hat outputs.",
33
+ mono=mono,
34
+ )
35
+
36
+
37
+ def help_validate(
38
+ tokens: List[str],
39
+ mono: bool,
40
+ ) -> str:
41
+ return show_usage(
42
+ [
43
+ "@sbc",
44
+ "hat",
45
+ "validate",
46
+ ],
47
+ "validate hat.",
48
+ mono=mono,
49
+ )
50
+
51
+
52
+ help_functions = {
53
+ "input": help_input,
54
+ "output": help_output,
55
+ "validate": help_validate,
56
+ }
@@ -0,0 +1,39 @@
1
+ from typing import List
2
+
3
+ from blue_options.terminal import show_usage, xtra
4
+
5
+
6
+ def help_capture(
7
+ tokens: List[str],
8
+ mono: bool,
9
+ ) -> str:
10
+ return show_usage(
11
+ [
12
+ "@sbc",
13
+ "lepton",
14
+ "capture",
15
+ ],
16
+ "lepton.capture.",
17
+ mono=mono,
18
+ )
19
+
20
+
21
+ def help_preview(
22
+ tokens: List[str],
23
+ mono: bool,
24
+ ) -> str:
25
+ return show_usage(
26
+ [
27
+ "@sbc",
28
+ "lepton",
29
+ "preview",
30
+ ],
31
+ "lepton.preview.",
32
+ mono=mono,
33
+ )
34
+
35
+
36
+ help_functions = {
37
+ "capture": help_capture,
38
+ "preview": help_preview,
39
+ }
@@ -0,0 +1,23 @@
1
+ from typing import List
2
+
3
+ from blue_options.terminal import show_usage
4
+
5
+
6
+ def help_validate(
7
+ tokens: List[str],
8
+ mono: bool,
9
+ ) -> str:
10
+ return show_usage(
11
+ [
12
+ "@sbc",
13
+ "scroll_phat_hd",
14
+ "validate",
15
+ ],
16
+ "validate scroll_phat_hd.",
17
+ mono=mono,
18
+ )
19
+
20
+
21
+ help_functions = {
22
+ "validate": help_validate,
23
+ }
@@ -0,0 +1,26 @@
1
+ from typing import List
2
+
3
+ from blue_options.terminal import show_usage, xtra
4
+
5
+
6
+ def help_session_start(
7
+ tokens: List[str],
8
+ mono: bool,
9
+ ) -> str:
10
+ options = xtra("dryrun,sudo,~upload", mono=mono)
11
+
12
+ return show_usage(
13
+ [
14
+ "@sbc",
15
+ "session",
16
+ "start",
17
+ f"[{options}]",
18
+ ],
19
+ "start an @sbc session.",
20
+ mono=mono,
21
+ )
22
+
23
+
24
+ help_functions = {
25
+ "start": help_session_start,
26
+ }
@@ -0,0 +1,26 @@
1
+ from typing import List
2
+
3
+ from blue_options.terminal import show_usage, xtra
4
+
5
+
6
+ def help_validate(
7
+ tokens: List[str],
8
+ mono: bool,
9
+ ) -> str:
10
+ options = "button | leds"
11
+
12
+ return show_usage(
13
+ [
14
+ "@sbc",
15
+ "sparkfun_top_phat",
16
+ "validate",
17
+ f"[{options}]",
18
+ ],
19
+ "validate sparkfun_top_phat.",
20
+ mono=mono,
21
+ )
22
+
23
+
24
+ help_functions = {
25
+ "validate": help_validate,
26
+ }
@@ -0,0 +1,23 @@
1
+ from typing import List
2
+
3
+ from blue_options.terminal import show_usage, xtra
4
+
5
+
6
+ def help_validate(
7
+ tokens: List[str],
8
+ mono: bool,
9
+ ) -> str:
10
+ return show_usage(
11
+ [
12
+ "@sbc",
13
+ "unicorn_16x16",
14
+ "validate",
15
+ ],
16
+ "validate unicorn_16x16.",
17
+ mono=mono,
18
+ )
19
+
20
+
21
+ help_functions = {
22
+ "validate": help_validate,
23
+ }
bluer_sbc/host.py ADDED
@@ -0,0 +1,11 @@
1
+ from typing import List
2
+
3
+ from bluer_ai.host import signature as abcli_signature
4
+
5
+ from bluer_sbc import fullname
6
+
7
+
8
+ def signature() -> List[str]:
9
+ return [
10
+ fullname(),
11
+ ] + abcli_signature()
@@ -0,0 +1,16 @@
1
+ from blueness import module
2
+
3
+ from bluer_sbc import NAME
4
+ from bluer_sbc import env
5
+ from bluer_sbc.logger import logger
6
+
7
+ NAME = module.name(__file__, NAME)
8
+
9
+
10
+ imager_name = env.BLUER_SBC_SESSION_IMAGER
11
+ if imager_name == "lepton":
12
+ from bluer_sbc.imager.lepton import instance as imager
13
+ else:
14
+ from bluer_sbc.imager.camera import instance as imager
15
+
16
+ logger.info(f"{NAME}: {imager_name}: {imager.__class__.__name__}")
@@ -0,0 +1,3 @@
1
+ from bluer_sbc.imager.camera.classes import Camera
2
+
3
+ instance = Camera()
@@ -0,0 +1,69 @@
1
+ import argparse
2
+
3
+ from blueness import module
4
+ from bluer_options import string
5
+ from bluer_objects.env import abcli_object_name
6
+
7
+ from bluer_sbc import NAME
8
+ from bluer_sbc.imager.camera import instance as camera
9
+ from bluer_sbc.logger import logger
10
+
11
+ NAME = module.name(__file__, NAME)
12
+
13
+
14
+ parser = argparse.ArgumentParser(NAME)
15
+ parser.add_argument(
16
+ "task",
17
+ type=str,
18
+ help="capture | capture_video | preview",
19
+ )
20
+ parser.add_argument(
21
+ "--object_name",
22
+ type=str,
23
+ default=abcli_object_name,
24
+ )
25
+ parser.add_argument(
26
+ "--filename",
27
+ type=str,
28
+ default="",
29
+ )
30
+ parser.add_argument(
31
+ "--length",
32
+ type=int,
33
+ default=0,
34
+ )
35
+ parser.add_argument(
36
+ "--output_path",
37
+ type=str,
38
+ default="",
39
+ )
40
+ parser.add_argument(
41
+ "--preview",
42
+ type=int,
43
+ default=1,
44
+ help="0 | 1",
45
+ )
46
+ args = parser.parse_args()
47
+
48
+ success = False
49
+ if args.task == "capture":
50
+ success, image = camera.capture(
51
+ filename=args.filename if args.filename else f"{string.timestamp()}.png",
52
+ object_name=args.object_name,
53
+ )
54
+ elif args.task == "capture_video":
55
+ success = camera.capture_video(
56
+ filename=args.filename if args.filename else f"{string.timestamp()}.h264",
57
+ object_name=args.object_name,
58
+ length=args.length if args.length else 10,
59
+ preview=args.preview,
60
+ resolution=(728, 600),
61
+ )
62
+ elif args.task == "preview":
63
+ success = camera.preview(length=args.length if args.length else -1)
64
+
65
+ else:
66
+ logger.error(f"-{NAME}: {args.task}: command not found.")
67
+
68
+ if not success:
69
+ logger.error(f"-{NAME}: {args.task}: failed.")
@@ -0,0 +1,259 @@
1
+ import cv2
2
+ from typing import Tuple
3
+ import numpy as np
4
+ from time import sleep
5
+
6
+ from blueness import module
7
+ from blue_options import string
8
+ from blue_options import host
9
+ from bluer_options.timer import Timer
10
+ from bluer_options.logger import crash_report
11
+ from bluer_objects import file, objects
12
+
13
+ from bluer_sbc import env
14
+ from bluer_sbc import NAME
15
+ from bluer_sbc.hardware import hardware
16
+ from bluer_sbc.imager.classes import Imager
17
+ from bluer_sbc.logger import logger
18
+
19
+ NAME = module.name(__file__, NAME)
20
+
21
+
22
+ class Camera(Imager):
23
+ def __init__(self):
24
+ self.device = None
25
+ self.resolution = []
26
+
27
+ def capture(
28
+ self,
29
+ close_after: bool = True,
30
+ log: bool = True,
31
+ open_before: bool = True,
32
+ filename: str = "",
33
+ object_name: str = "",
34
+ ) -> Tuple[bool, np.ndarray]:
35
+ success = False
36
+ image = np.ones((1, 1, 3), dtype=np.uint8) * 127
37
+
38
+ if open_before:
39
+ if not self.open():
40
+ return success, image
41
+
42
+ if self.device is None:
43
+ return success, image
44
+
45
+ if host.is_rpi():
46
+ temp = file.auxiliary("camera", "png")
47
+ try:
48
+ self.device.capture(temp)
49
+ success = True
50
+ except Exception as e:
51
+ crash_report(e)
52
+
53
+ if success:
54
+ success, image = file.load_image(temp)
55
+ else:
56
+ try:
57
+ success, image = self.device.read()
58
+
59
+ if success:
60
+ image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
61
+
62
+ except Exception as e:
63
+ crash_report(e)
64
+
65
+ if close_after:
66
+ self.close()
67
+
68
+ if success and log:
69
+ logger.info(f"{NAME}.capture(): {string.pretty_shape_of_matrix(image)}")
70
+
71
+ if success and filename:
72
+ success = file.save_image(
73
+ filename=objects.path_of(
74
+ object_name=object_name,
75
+ filename=filename,
76
+ ),
77
+ image=image,
78
+ log=log,
79
+ )
80
+
81
+ return success, image
82
+
83
+ # https://projects.raspberrypi.org/en/projects/getting-started-with-picamera/6
84
+ def capture_video(
85
+ self,
86
+ filename: str,
87
+ object_name: str,
88
+ length: int = 10, # in seconds
89
+ preview: bool = True,
90
+ pulse: bool = True,
91
+ resolution=None,
92
+ ) -> bool:
93
+ if not host.is_rpi():
94
+ logger.error(f"{NAME}.capture_video() only works on rpi.")
95
+ return False
96
+
97
+ if not self.open(resolution=resolution):
98
+ return False
99
+
100
+ full_filename = objects.path_of(
101
+ object_name=object_name,
102
+ filename=filename,
103
+ )
104
+
105
+ success = True
106
+ try:
107
+ if preview:
108
+ self.device.start_preview()
109
+
110
+ self.device.start_recording(full_filename)
111
+ if pulse:
112
+ for _ in range(int(10 * length)):
113
+ hardware.pulse("outputs")
114
+ sleep(0.1)
115
+ else:
116
+ sleep(length)
117
+ self.device.stop_recording()
118
+
119
+ if preview:
120
+ self.device.stop_preview()
121
+ except Exception as e:
122
+ crash_report(e)
123
+ success = False
124
+
125
+ if not self.close():
126
+ return False
127
+
128
+ if success:
129
+ logger.info(
130
+ "{}.capture_video(): {} -{}-> {}".format(
131
+ NAME,
132
+ string.pretty_duration(length),
133
+ string.pretty_bytes(file.size(full_filename)),
134
+ filename,
135
+ )
136
+ )
137
+
138
+ return success
139
+
140
+ def close(self, log: bool = True) -> bool:
141
+ if self.device is None:
142
+ logger.warning(f"{NAME}.close(): device is {self.device}, failed.")
143
+ return False
144
+
145
+ success = False
146
+ try:
147
+ if host.is_rpi():
148
+ self.device.close()
149
+ else:
150
+ self.device.release()
151
+ success = True
152
+ except Exception as e:
153
+ crash_report(e)
154
+ return False
155
+
156
+ self.device = None
157
+
158
+ if log:
159
+ logger.info(f"{NAME}.close().")
160
+
161
+ return success
162
+
163
+ def get_resolution(self):
164
+ try:
165
+ if host.is_rpi():
166
+ from picamera import PiCamera
167
+
168
+ return [value for value in self.device.resolution]
169
+ else:
170
+ return [
171
+ int(self.device.get(const))
172
+ for const in [cv2.CAP_PROP_FRAME_HEIGHT, cv2.CAP_PROP_FRAME_WIDTH]
173
+ ]
174
+ except Exception as e:
175
+ crash_report(e)
176
+ return []
177
+
178
+ def open(
179
+ self,
180
+ log: bool = True,
181
+ resolution=None,
182
+ ) -> bool:
183
+ try:
184
+ if host.is_rpi():
185
+ from picamera import PiCamera
186
+
187
+ self.device = PiCamera()
188
+ self.device.rotation = env.BLUER_SBC_CAMERA_ROTATION
189
+
190
+ # https://projects.raspberrypi.org/en/projects/getting-started-with-picamera/7
191
+ self.device.resolution = (
192
+ (
193
+ (2592, 1944)
194
+ if env.BLUER_SBC_CAMERA_HI_RES
195
+ else (
196
+ env.BLUER_SBC_CAMERA_WIDTH,
197
+ env.BLUER_SBC_CAMERA_HEIGHT,
198
+ )
199
+ )
200
+ if resolution is None
201
+ else resolution
202
+ )
203
+ else:
204
+ self.device = cv2.VideoCapture(0)
205
+
206
+ # https://stackoverflow.com/a/31464688
207
+ self.device.set(cv2.CAP_PROP_FRAME_WIDTH, 10000)
208
+ self.device.set(cv2.CAP_PROP_FRAME_HEIGHT, 10000)
209
+
210
+ self.resolution = self.get_resolution()
211
+
212
+ if log:
213
+ logger.info(f"{NAME}.open({string.pretty_shape(self.resolution)})")
214
+
215
+ return True
216
+ except Exception as e:
217
+ crash_report(e)
218
+ return False
219
+
220
+ def preview(
221
+ self,
222
+ length: float = -1,
223
+ ) -> bool:
224
+ logger.info(
225
+ "{}.preview{} ... | press q or e to quit ...".format(
226
+ NAME,
227
+ "[{}]".format("" if length == -1 else string.pretty_duration(length)),
228
+ )
229
+ )
230
+
231
+ hardware.sign_images = False
232
+ timer = Timer(length, "preview")
233
+ try:
234
+ self.open(
235
+ log=True,
236
+ resolution=(320, 240),
237
+ )
238
+
239
+ while not hardware.pressed("qe"):
240
+ _, image = self.capture(
241
+ close_after=False,
242
+ log=False,
243
+ open_before=False,
244
+ )
245
+ hardware.update_screen(image, None, [])
246
+
247
+ if timer.tick(wait=True):
248
+ logger.info(
249
+ "{} is up, quitting.".format(string.pretty_duration(length))
250
+ )
251
+ break
252
+
253
+ except KeyboardInterrupt:
254
+ logger.info("Ctrl+C, stopping.")
255
+
256
+ finally:
257
+ self.close(log=True)
258
+
259
+ return True
@@ -0,0 +1,30 @@
1
+ specifications = {
2
+ "rpi-ir": {
3
+ "height": 1944,
4
+ "width": 2592,
5
+ "diagonal": 3240,
6
+ "fov": {
7
+ "diagonal": 80,
8
+ "horizontal": 64,
9
+ "vertical": 48,
10
+ },
11
+ },
12
+ "rpi-noir": {
13
+ "height": 1944,
14
+ "width": 2592,
15
+ "diagonal": 3240,
16
+ "fov": {
17
+ "horizontal": 62.2,
18
+ "vertical": 48.8,
19
+ },
20
+ },
21
+ "rpi-rgb": {
22
+ "height": 1944,
23
+ "width": 2592,
24
+ "diagonal": 3240,
25
+ "fov": {
26
+ "horizontal": 62.2,
27
+ "vertical": 48.8,
28
+ },
29
+ },
30
+ }