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,136 @@
1
+ import math
2
+ import time
3
+
4
+ from bluer_sbc.hardware.hardware import Hardware
5
+ from bluer_sbc.logger import logger
6
+
7
+
8
+ class Abstract_Hat(Hardware):
9
+ def __init__(self):
10
+ super().__init__()
11
+
12
+ self.switch_pin = -1
13
+
14
+ self.green_switch_pin = -1
15
+ self.red_switch_pin = -1
16
+ self.trigger_pin = -1
17
+
18
+ self.looper_pin = -1 # red led
19
+ self.incoming_pin = -1 # yellow led
20
+ self.data_pin = -1 # green led
21
+ self.outgoing_pin = -1 # blue led
22
+
23
+ self.green_led_pin = -1
24
+ self.red_led_pin = -1
25
+
26
+ self.pin_history = {}
27
+
28
+ def activated(self, pin):
29
+ """
30
+ is pin activated?
31
+ :param pin: pin number
32
+ :return: True / False
33
+ """
34
+ if pin in self.input_pins:
35
+ return self.input(pin) == False
36
+
37
+ return False
38
+
39
+ def input(self, pin):
40
+ """
41
+ read pin input.
42
+ :param pin: pin number
43
+ :return: True / False
44
+ """
45
+ return True
46
+
47
+ @property
48
+ def input_pins(self):
49
+ return [
50
+ pin
51
+ for pin in [
52
+ self.switch_pin,
53
+ self.green_switch_pin,
54
+ self.red_switch_pin,
55
+ self.trigger_pin,
56
+ ]
57
+ if pin != -1
58
+ ]
59
+
60
+ def output(self, pin, output):
61
+ """
62
+ set pin to output
63
+ :param pin: pin number
64
+ :param output: True / False
65
+ :return: self
66
+ """
67
+ return self
68
+
69
+ @property
70
+ def output_pins(self):
71
+ return [
72
+ pin
73
+ for pin in [
74
+ self.looper_pin,
75
+ self.incoming_pin,
76
+ self.data_pin,
77
+ self.outgoing_pin,
78
+ self.green_led_pin,
79
+ self.red_led_pin,
80
+ ]
81
+ if pin != -1
82
+ ]
83
+
84
+ def pulse(self, pin=None, frequency=None):
85
+ """
86
+ pulse pin.
87
+ :param pin: pin number / "data" / "incoming" / "loop" / "outputs"
88
+ :param frequency: frequency
89
+ :return: self
90
+ """
91
+ super().pulse(pin, frequency)
92
+
93
+ if pin == "data":
94
+ pin = self.data_pin
95
+
96
+ if pin == "incoming":
97
+ pin = self.incoming_pin
98
+
99
+ if pin == "loop":
100
+ pin = self.looper_pin
101
+
102
+ if pin == "outputs":
103
+ for index, pin in enumerate(self.output_pins):
104
+ self.pulse(pin, index)
105
+
106
+ return self
107
+
108
+ if pin is None:
109
+ for pin in self.output_pins:
110
+ self.pulse(pin, frequency)
111
+
112
+ return self
113
+
114
+ self.pin_history[pin] = (
115
+ not bool(self.pin_history.get(pin, False))
116
+ if frequency is None
117
+ else (lambda x: x - math.floor(x))(time.time() * (10 + frequency)) >= 0.5
118
+ )
119
+
120
+ return self.output(pin, self.pin_history[pin])
121
+
122
+ def release(self):
123
+ """
124
+ release self
125
+ :return: self
126
+ """
127
+ return self
128
+
129
+ def setup(self, pin, what, pull_up_down=None):
130
+ """
131
+ Set up pin.
132
+ :param pin: pin number
133
+ :param what: "input" / "output"
134
+ :return: self
135
+ """
136
+ return self
@@ -0,0 +1,161 @@
1
+ import time
2
+
3
+ from blueness import module
4
+ from bluer_options import string
5
+ from bluer_options import host
6
+
7
+ from bluer_sbc import NAME
8
+ from bluer_sbc.hardware.hat.abstract import Abstract_Hat
9
+ from bluer_sbc.logger import logger
10
+
11
+ NAME = module.name(__file__, NAME)
12
+
13
+
14
+ class Prototype_Hat(Abstract_Hat):
15
+ def __init__(self):
16
+ super().__init__()
17
+
18
+ self.switch_on_time = None
19
+
20
+ self.is_jetson = host.is_jetson()
21
+ self.is_rpi = host.is_rpi()
22
+
23
+ if self.is_rpi:
24
+ import RPi.GPIO as GPIO
25
+
26
+ self.switch_pin = 12
27
+
28
+ self.green_switch_pin = 16
29
+ self.red_switch_pin = 13
30
+
31
+ self.looper_pin = 22 # red led
32
+ self.incoming_pin = 18 # yellow led
33
+ self.data_pin = 36 # green led
34
+ self.outgoing_pin = 40 # blue led
35
+
36
+ self.green_led_pin = 15
37
+ self.red_led_pin = 7
38
+
39
+ GPIO.setmode(GPIO.BOARD) # numbers GPIOs by physical location
40
+
41
+ if self.is_jetson:
42
+ import Jetson.GPIO as GPIO
43
+
44
+ self.switch_pin = 7
45
+
46
+ self.trigger_pin = 12
47
+ self.looper_pin = 29 # red led
48
+ self.incoming_pin = 31 # yellow led
49
+ self.data_pin = 32 # green led
50
+ self.outgoing_pin = 33 # blue led
51
+
52
+ GPIO.setmode(GPIO.BOARD) # numbers GPIOs by physical location
53
+
54
+ # http://razzpisampler.oreilly.com/ch07.html
55
+ for pin in self.input_pins:
56
+ self.setup(pin, "input", GPIO.PUD_UP)
57
+
58
+ for pin in self.output_pins:
59
+ self.setup(pin, "output")
60
+ self.output(pin, False)
61
+
62
+ def clock(self):
63
+ super().clock()
64
+
65
+ if self.activated(self.switch_pin):
66
+ if self.switch_on_time is None:
67
+ self.switch_on_time = time.time()
68
+ logger.info(f"{NAME}: switch_on_time was set.")
69
+ else:
70
+ self.switch_on_time = None
71
+
72
+ if self.switch_on_time is not None:
73
+ self.pulse("outputs")
74
+
75
+ if time.time() - self.switch_on_time > 10:
76
+ self.key_buffer += ["s"]
77
+
78
+ return self
79
+
80
+ def input(self, pin):
81
+ if pin == -1:
82
+ return True
83
+
84
+ if self.is_rpi:
85
+ import RPi.GPIO as GPIO
86
+ elif self.is_jetson:
87
+ import Jetson.GPIO as GPIO
88
+ else:
89
+ return False
90
+
91
+ return GPIO.input(pin) == GPIO.HIGH
92
+
93
+ def output(self, pin, output):
94
+ if pin == -1:
95
+ return self
96
+
97
+ if self.is_rpi:
98
+ import RPi.GPIO as GPIO
99
+ elif self.is_jetson:
100
+ import Jetson.GPIO as GPIO
101
+ else:
102
+ return self
103
+
104
+ GPIO.output(pin, GPIO.HIGH if output else GPIO.LOW)
105
+ return self
106
+
107
+ def release(self):
108
+ if self.is_rpi:
109
+ import RPi.GPIO as GPIO
110
+ elif self.is_jetson:
111
+ import Jetson.GPIO as GPIO
112
+ else:
113
+ return self
114
+
115
+ logger.info(f"{self.__class__.__name__}.release()")
116
+
117
+ GPIO.cleanup()
118
+
119
+ return self
120
+
121
+ def setup(self, pin, what, pull_up_down=None):
122
+ if pin == -1:
123
+ return self
124
+
125
+ if self.is_rpi:
126
+ import RPi.GPIO as GPIO
127
+ elif self.is_jetson:
128
+ import Jetson.GPIO as GPIO
129
+ else:
130
+ return self
131
+
132
+ if what == "output":
133
+ what = GPIO.OUT
134
+ elif what == "input":
135
+ what = GPIO.IN
136
+ else:
137
+ raise NameError(
138
+ f"{self.__class__.__name__}.setup({pin}): unknown what: {what}."
139
+ )
140
+
141
+ if pull_up_down is None:
142
+ GPIO.setup(pin, what)
143
+ else:
144
+ GPIO.setup(pin, what, pull_up_down=pull_up_down)
145
+
146
+ return self
147
+
148
+ def signature(self):
149
+ return super().signature() + (
150
+ [
151
+ "switch:{}".format(
152
+ string.pretty_duration(
153
+ time.time() - self.switch_on_time,
154
+ largest=True,
155
+ short=True,
156
+ )
157
+ )
158
+ ]
159
+ if self.switch_on_time is not None
160
+ else []
161
+ )
@@ -0,0 +1,17 @@
1
+ import copy
2
+
3
+ from bluer_sbc.hardware.hardware import Hardware
4
+
5
+
6
+ class Screen(Hardware):
7
+ def __init__(self):
8
+ super().__init__()
9
+ self.size = None
10
+
11
+ self.buffer = None
12
+
13
+ def update_screen(self, image, session, header):
14
+ if self.animated:
15
+ self.buffer = copy.deepcopy(image)
16
+
17
+ return super().update_screen(image, session, header)
@@ -0,0 +1,35 @@
1
+ import cv2
2
+ import time
3
+
4
+ from bluer_sbc.hardware.screen import Screen
5
+
6
+
7
+ class Scroll_Phat_HD(Screen):
8
+ def __init__(self):
9
+ super(Scroll_Phat_HD, self).__init__()
10
+ self.size = (7, 17)
11
+ self.animated = True
12
+
13
+ def update_screen(self, image, session, header):
14
+ import scrollphathd
15
+
16
+ image = cv2.resize(
17
+ image,
18
+ self.size,
19
+ )
20
+
21
+ super().update_screen(image, session, header)
22
+
23
+ image = cv2.cvtColor(
24
+ image,
25
+ cv2.COLOR_BGR2GRAY,
26
+ )
27
+
28
+ for y in range(0, 17):
29
+ for x in range(0, 7):
30
+ scrollphathd.set_pixel(y, x, image[y, x] / 255.0)
31
+
32
+ time.sleep(0.01)
33
+ scrollphathd.show()
34
+
35
+ return self
File without changes
@@ -0,0 +1,51 @@
1
+ import argparse
2
+ import time
3
+
4
+ from blueness import module
5
+
6
+ from bluer_sbc import NAME
7
+ from bluer_sbc.hardware.sparkfun_top_phat.classes import Sparkfun_Top_phat
8
+ from bluer_sbc.logger import logger
9
+
10
+ NAME = module.name(__file__, NAME)
11
+
12
+ parser = argparse.ArgumentParser(NAME)
13
+ parser.add_argument(
14
+ "task",
15
+ type=str,
16
+ default="",
17
+ help="validate_leds",
18
+ )
19
+ args = parser.parse_args()
20
+
21
+ hardware = Sparkfun_Top_phat()
22
+
23
+ success = False
24
+ if args.task == "validate_leds":
25
+ logger.info("loop started (Ctrl+C to stop)")
26
+ offset = 0
27
+ # https://stackoverflow.com/a/18994932/10917551
28
+ try:
29
+ while True:
30
+ for index in range(hardware.pixel_count):
31
+ hardware.pixels[index] = tuple(
32
+ int(thing * hardware.intensity)
33
+ for thing in hardware.colormap[
34
+ (index + offset) % hardware.pixel_count
35
+ ][:3]
36
+ )
37
+
38
+ offset += 1
39
+
40
+ hardware.pixels.show()
41
+ time.sleep(0.1)
42
+ except KeyboardInterrupt:
43
+ logger.info("Ctrl+C, stopping.")
44
+ finally:
45
+ hardware.release()
46
+ success = True
47
+ else:
48
+ logger.error(f"-{NAME}: {args.task}: command not found.")
49
+
50
+ if not success:
51
+ logger.error(f"-{NAME}: {args.task}: failed.")
@@ -0,0 +1,104 @@
1
+ import cv2
2
+ import time
3
+ from matplotlib import cm
4
+
5
+ from blueness import module
6
+
7
+ from bluer_sbc import NAME
8
+ from bluer_sbc.hardware.screen import Screen
9
+ from bluer_sbc.logger import logger
10
+
11
+ NAME = module.name(__file__, NAME)
12
+
13
+
14
+ class Sparkfun_Top_phat(Screen):
15
+ def __init__(self):
16
+ super(Sparkfun_Top_phat, self).__init__()
17
+ self.size = (7, 17)
18
+ self.animated = False
19
+
20
+ import board
21
+ import neopixel
22
+
23
+ # https://learn.sparkfun.com/tutorials/sparkfun-top-phat-hookup-guide/ws2812b-leds
24
+ self.pixel_count = 6
25
+ self.pixels = neopixel.NeoPixel(
26
+ board.D12,
27
+ self.pixel_count,
28
+ auto_write=False,
29
+ )
30
+
31
+ self.intensity = 48
32
+
33
+ # https://matplotlib.org/stable/tutorials/colors/colormaps.html
34
+ self.colormap = cm.get_cmap("copper", self.pixel_count)(range(self.pixel_count))
35
+
36
+ self.pulse_cycle = 0
37
+
38
+ # https://github.com/sparkfun/Top_pHAT_Button_Py/blob/main/examples/top_phat_button_ex1.py
39
+ import top_phat_button
40
+
41
+ self.buttons = top_phat_button.ToppHATButton()
42
+ logger.info(f"{NAME}.connection status: {self.buttons.is_connected()}")
43
+
44
+ self.buttons.pressed_interrupt_enable = False
45
+ self.buttons.clicked_interrupt_enable = False
46
+
47
+ def clock(self):
48
+ super().clock()
49
+
50
+ self.buttons.button_pressed # These functions must be called to update button variables to their latest setting
51
+ self.buttons.button_clicked # These functions must be called to update button variables to their latest setting
52
+
53
+ if self.buttons.a_clicked == True:
54
+ logger.info(f"{NAME}: a clicked: update.")
55
+ self.key_buffer += ["u"]
56
+
57
+ if self.buttons.b_clicked == True:
58
+ logger.info(f"{NAME}: b clicked: shutdown.")
59
+ self.key_buffer += ["s"]
60
+
61
+ if self.buttons.center_clicked == True:
62
+ logger.info(f"{NAME}: center clicked.")
63
+ self.key_buffer += [" "]
64
+
65
+ if self.buttons.up_clicked == True:
66
+ logger.info(f"{NAME}: up clicked.")
67
+ self.intensity = min(255, 2 * self.intensity)
68
+
69
+ if self.buttons.down_clicked == True:
70
+ logger.info(f"{NAME}: down clicked.")
71
+ self.intensity = max(1, self.intensity // 2)
72
+
73
+ return self
74
+
75
+ def pulse(self, pin=None, frequency=None):
76
+ super().pulse(pin, frequency)
77
+
78
+ for index in range(self.pixel_count):
79
+ self.pixels[index] = tuple(
80
+ int(thing * self.intensity)
81
+ for thing in self.colormap[
82
+ (index + int(self.pulse_cycle / 10)) % self.pixel_count
83
+ ][:3]
84
+ )
85
+
86
+ self.pixels.show()
87
+
88
+ self.pulse_cycle += 1
89
+
90
+ return self
91
+
92
+ def release(self):
93
+ super().release()
94
+ for index in range(self.pixel_count):
95
+ self.pixels[index] = 3 * (0,)
96
+ self.pixels.show()
97
+ time.sleep(0.1)
98
+
99
+ def update_screen(self, image, session, header):
100
+ image = cv2.resize(image, self.size)
101
+
102
+ super().update_screen(image, session, header)
103
+
104
+ return self
@@ -0,0 +1,44 @@
1
+ import cv2
2
+
3
+ from bluer_sbc.hardware.screen import Screen
4
+
5
+
6
+ class Unicorn_16x16(Screen):
7
+ def __init__(self):
8
+ super().__init__()
9
+ self.size = (16, 16)
10
+ self.animated = True
11
+
12
+ def release(self):
13
+ super().release()
14
+
15
+ import unicornhathd
16
+
17
+ unicornhathd.off()
18
+
19
+ def update_screen(self, image, session, header):
20
+ import unicornhathd
21
+
22
+ image = cv2.rotate(
23
+ cv2.resize(
24
+ image,
25
+ self.size,
26
+ ),
27
+ cv2.ROTATE_90_CLOCKWISE,
28
+ )
29
+
30
+ super().update_screen(image, session, header)
31
+
32
+ for x in range(0, 16):
33
+ for y in range(0, 16):
34
+ unicornhathd.set_pixel(
35
+ x,
36
+ y,
37
+ image[x, y, 0],
38
+ image[x, y, 1],
39
+ image[x, y, 2],
40
+ )
41
+
42
+ unicornhathd.show()
43
+
44
+ return self
File without changes
@@ -0,0 +1,10 @@
1
+ from blueness import module
2
+ from bluer_options.help.functions import help_main
3
+
4
+ from bluer_sbc import NAME
5
+ from bluer_sbc.help.functions import help_functions
6
+
7
+ NAME = module.name(__file__, NAME)
8
+
9
+
10
+ help_main(NAME, help_functions)
@@ -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
+ "adafruit_rgb_matrix",
14
+ "validate",
15
+ ],
16
+ "validate adafruit_rgb_matrix.",
17
+ mono=mono,
18
+ )
19
+
20
+
21
+ help_functions = {
22
+ "validate": help_validate,
23
+ }
@@ -0,0 +1,71 @@
1
+ from typing import List
2
+
3
+ from blue_options.terminal import show_usage
4
+
5
+
6
+ def help_capture_image(
7
+ tokens: List[str],
8
+ mono: bool,
9
+ ) -> str:
10
+ return show_usage(
11
+ [
12
+ "@sbc",
13
+ "camera",
14
+ "capture",
15
+ "image",
16
+ ],
17
+ "capture an image.",
18
+ mono=mono,
19
+ )
20
+
21
+
22
+ def help_capture_video(
23
+ tokens: List[str],
24
+ mono: bool,
25
+ ) -> str:
26
+ args = [
27
+ "[--length 10]",
28
+ "[--preview 1]",
29
+ ]
30
+
31
+ return show_usage(
32
+ [
33
+ "@sbc",
34
+ "camera",
35
+ "capture",
36
+ "video",
37
+ ]
38
+ + args,
39
+ "capture a video",
40
+ mono=mono,
41
+ )
42
+
43
+
44
+ def help_preview(
45
+ tokens: List[str],
46
+ mono: bool,
47
+ ) -> str:
48
+ args = [
49
+ "[--length 10]",
50
+ ]
51
+
52
+ return show_usage(
53
+ [
54
+ "@sbc",
55
+ "camera",
56
+ "preview",
57
+ "[-]",
58
+ ]
59
+ + args,
60
+ "preview.",
61
+ mono=mono,
62
+ )
63
+
64
+
65
+ help_functions = {
66
+ "capture": {
67
+ "image": help_capture_image,
68
+ "video": help_capture_video,
69
+ },
70
+ "preview": help_preview,
71
+ }