tjbot-ce 3.0.0__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 (67) hide show
  1. tjbot/__init__.py +29 -0
  2. tjbot/camera/__init__.py +17 -0
  3. tjbot/camera/camera.py +168 -0
  4. tjbot/config/__init__.py +49 -0
  5. tjbot/config/config_types.py +251 -0
  6. tjbot/config/tjbot_config.py +357 -0
  7. tjbot/config/vendor/model-registry.yaml +275 -0
  8. tjbot/config/vendor/tjbot-config.schema.yaml +792 -0
  9. tjbot/config/vendor/tjbot.default.toml +452 -0
  10. tjbot/led/__init__.py +19 -0
  11. tjbot/led/led_common_anode.py +47 -0
  12. tjbot/led/led_neopixel.py +248 -0
  13. tjbot/led/led_neopixel_spi.py +107 -0
  14. tjbot/led/led_neopixel_ws281x.py +156 -0
  15. tjbot/microphone/__init__.py +17 -0
  16. tjbot/microphone/microphone.py +263 -0
  17. tjbot/rpi_drivers/__init__.py +28 -0
  18. tjbot/rpi_drivers/rpi3_driver.py +170 -0
  19. tjbot/rpi_drivers/rpi4_driver.py +108 -0
  20. tjbot/rpi_drivers/rpi5_driver.py +97 -0
  21. tjbot/rpi_drivers/rpi_detect.py +56 -0
  22. tjbot/rpi_drivers/rpi_driver.py +430 -0
  23. tjbot/servo/__init__.py +24 -0
  24. tjbot/servo/servo_constants.py +26 -0
  25. tjbot/servo/servo_lgpio.py +152 -0
  26. tjbot/speaker/__init__.py +18 -0
  27. tjbot/speaker/audio_player.py +45 -0
  28. tjbot/speaker/speaker.py +123 -0
  29. tjbot/stt/__init__.py +27 -0
  30. tjbot/stt/backends/__init__.py +25 -0
  31. tjbot/stt/backends/azure_stt.py +351 -0
  32. tjbot/stt/backends/google_cloud_stt.py +357 -0
  33. tjbot/stt/backends/sherpa_onnx_stt.py +490 -0
  34. tjbot/stt/backends/watson_stt.py +339 -0
  35. tjbot/stt/stt.py +112 -0
  36. tjbot/stt/stt_engine.py +138 -0
  37. tjbot/stt/stt_utils.py +155 -0
  38. tjbot/tjbot.py +723 -0
  39. tjbot/tts/__init__.py +18 -0
  40. tjbot/tts/backends/__init__.py +25 -0
  41. tjbot/tts/backends/azure_tts.py +121 -0
  42. tjbot/tts/backends/google_cloud_tts.py +147 -0
  43. tjbot/tts/backends/ibm_watson_tts.py +99 -0
  44. tjbot/tts/backends/sherpa_onnx_tts.py +164 -0
  45. tjbot/tts/tts.py +74 -0
  46. tjbot/tts/tts_engine.py +87 -0
  47. tjbot/utils/__init__.py +51 -0
  48. tjbot/utils/colors.py +147 -0
  49. tjbot/utils/colors.yaml +47 -0
  50. tjbot/utils/constants.py +31 -0
  51. tjbot/utils/credentials.py +95 -0
  52. tjbot/utils/errors.py +35 -0
  53. tjbot/utils/logging.py +115 -0
  54. tjbot/utils/model_registry.py +317 -0
  55. tjbot/utils/sherpa_runtime.py +65 -0
  56. tjbot/utils/utils.py +42 -0
  57. tjbot/vision/__init__.py +37 -0
  58. tjbot/vision/backends/__init__.py +19 -0
  59. tjbot/vision/backends/azure_vision.py +169 -0
  60. tjbot/vision/backends/google_cloud_vision.py +248 -0
  61. tjbot/vision/backends/onnx.py +736 -0
  62. tjbot/vision/vision.py +70 -0
  63. tjbot/vision/vision_engine.py +195 -0
  64. tjbot_ce-3.0.0.dist-info/METADATA +422 -0
  65. tjbot_ce-3.0.0.dist-info/RECORD +67 -0
  66. tjbot_ce-3.0.0.dist-info/WHEEL +4 -0
  67. tjbot_ce-3.0.0.dist-info/licenses/LICENSE +201 -0
tjbot/__init__.py ADDED
@@ -0,0 +1,29 @@
1
+ # Copyright 2026-present TJBot Contributors. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from typing import TYPE_CHECKING
16
+
17
+ if TYPE_CHECKING:
18
+ from .tjbot import TJBot
19
+
20
+
21
+ def __getattr__(name: str):
22
+ if name == "TJBot":
23
+ from .tjbot import TJBot
24
+
25
+ return TJBot
26
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
27
+
28
+
29
+ __all__ = ["TJBot"]
@@ -0,0 +1,17 @@
1
+ # Copyright 2026-present TJBot Contributors. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from .camera import CameraController
16
+
17
+ __all__ = ["CameraController"]
tjbot/camera/camera.py ADDED
@@ -0,0 +1,168 @@
1
+ # Copyright 2026-present TJBot Contributors. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import os
16
+ import subprocess
17
+ import tempfile
18
+ from typing import Tuple, Optional
19
+ import logging
20
+ from ..utils.errors import TJBotError
21
+
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ class CameraController:
27
+ """
28
+ TJBot Camera Controller.
29
+ Handles camera initialization and photo capture using rpicam-still.
30
+ """
31
+
32
+ def __init__(self):
33
+ self.resolution: Tuple[int, int] = (1920, 1080)
34
+ self.vertical_flip = False
35
+ self.horizontal_flip = False
36
+ self.capture_timeout = 500
37
+ self.zero_shutter_lag = False
38
+
39
+ def initialize(
40
+ self,
41
+ resolution: Tuple[int, int],
42
+ vertical_flip: bool,
43
+ horizontal_flip: bool,
44
+ capture_timeout: int = 500,
45
+ zero_shutter_lag: bool = False,
46
+ ) -> None:
47
+ """
48
+ Initialize camera settings.
49
+ :param resolution: (width, height) tuple.
50
+ :param vertical_flip: Whether to flip vertically.
51
+ :param horizontal_flip: Whether to flip horizontally.
52
+ """
53
+ self.resolution = resolution
54
+ self.vertical_flip = vertical_flip
55
+ self.horizontal_flip = horizontal_flip
56
+ self.capture_timeout = capture_timeout
57
+ self.zero_shutter_lag = zero_shutter_lag
58
+
59
+ logger.debug(
60
+ "Initialized camera with config: resolution=%sx%s, vertical_flip=%s, horizontal_flip=%s, capture_timeout=%sms, zero_shutter_lag=%s",
61
+ resolution[0],
62
+ resolution[1],
63
+ vertical_flip,
64
+ horizontal_flip,
65
+ capture_timeout,
66
+ zero_shutter_lag,
67
+ )
68
+
69
+ def build_camera_args(
70
+ self, output_path: str, encoding: Optional[str] = None
71
+ ) -> list[str]:
72
+ """
73
+ Build rpicam-still command arguments.
74
+ :param output_path: Output path or '-' for stdout.
75
+ :param encoding: Optional encoding format (for example 'jpg').
76
+ :return: List of command-line arguments.
77
+ """
78
+ args = [
79
+ "--output",
80
+ output_path,
81
+ "--width",
82
+ str(self.resolution[0]),
83
+ "--height",
84
+ str(self.resolution[1]),
85
+ "--nopreview",
86
+ "--camera",
87
+ "0",
88
+ ]
89
+
90
+ if encoding:
91
+ args.extend(["--encoding", encoding])
92
+
93
+ if self.vertical_flip:
94
+ args.append("--vflip")
95
+ if self.horizontal_flip:
96
+ args.append("--hflip")
97
+
98
+ if self.capture_timeout == 0:
99
+ args.append("--immediate")
100
+ else:
101
+ args.extend(["--timeout", str(self.capture_timeout)])
102
+
103
+ if self.zero_shutter_lag:
104
+ args.append("--zsl")
105
+
106
+ logger.debug("Built camera args: %s", " ".join(args))
107
+ return args
108
+
109
+ def capture_photo(self, at_path: Optional[str] = None) -> str:
110
+ """
111
+ Capture a photo by invoking rpicam-still.
112
+ :param at_path: Optional path to save the photo. If None, a temporary file will be used.
113
+ :return: Path to the saved photo.
114
+ """
115
+ if not at_path:
116
+ fd, at_path = tempfile.mkstemp(prefix="tjbot", suffix=".jpg")
117
+ os.close(fd)
118
+
119
+ logger.info("Capturing image at path: %s", at_path)
120
+ cmd = ["rpicam-still", *self.build_camera_args(at_path)]
121
+
122
+ try:
123
+ result = subprocess.run(
124
+ cmd,
125
+ check=True,
126
+ stdout=subprocess.PIPE,
127
+ stderr=subprocess.PIPE,
128
+ )
129
+ logger.debug(
130
+ "rpicam-still stdout: %s", result.stdout.decode(errors="replace")
131
+ )
132
+ return at_path
133
+ except (subprocess.CalledProcessError, FileNotFoundError) as err:
134
+ stderr = ""
135
+ if isinstance(err, subprocess.CalledProcessError):
136
+ stderr = (err.stderr or b"").decode(errors="replace")
137
+ message = stderr or str(err)
138
+ logger.error("rpicam-still error: %s", message)
139
+ raise TJBotError(message) from err
140
+
141
+ def capture_photo_buffer(self) -> bytes:
142
+ """
143
+ Capture a photo and return it as bytes.
144
+ :return: Photo bytes.
145
+ """
146
+ logger.info("Capturing image to buffer")
147
+ cmd = ["rpicam-still", *self.build_camera_args("-", "jpg")]
148
+
149
+ try:
150
+ result = subprocess.run(
151
+ cmd,
152
+ check=True,
153
+ stdout=subprocess.PIPE,
154
+ stderr=subprocess.PIPE,
155
+ )
156
+ logger.debug("Captured image buffer (%s bytes)", len(result.stdout))
157
+ return result.stdout
158
+ except (subprocess.CalledProcessError, FileNotFoundError) as err:
159
+ stderr = ""
160
+ if isinstance(err, subprocess.CalledProcessError):
161
+ stderr = (err.stderr or b"").decode(errors="replace")
162
+ message = stderr or str(err)
163
+ logger.error("rpicam-still error: %s", message)
164
+ raise TJBotError(message) from err
165
+
166
+ def cleanup(self) -> None:
167
+ """Clean up resources (no-op for direct process invocation)."""
168
+ logger.debug("CameraController cleanup (no-op for rpicam-still)")
@@ -0,0 +1,49 @@
1
+ # Copyright 2026-present TJBot Contributors. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from .config_types import (
16
+ TJBotConfigSchema,
17
+ LogConfig,
18
+ HardwareConfig,
19
+ ListenConfig,
20
+ SeeConfig,
21
+ SeeBackendConfig,
22
+ SeeBackendLocalConfig,
23
+ SeeBackendGoogleCloudConfig,
24
+ SeeBackendAzureConfig,
25
+ ShineConfig,
26
+ SpeakConfig,
27
+ WaveConfig,
28
+ STTBackendConfig,
29
+ TTSBackendConfig,
30
+ )
31
+ from .tjbot_config import TJBotConfig
32
+
33
+ __all__ = [
34
+ "TJBotConfig",
35
+ "TJBotConfigSchema",
36
+ "LogConfig",
37
+ "HardwareConfig",
38
+ "ListenConfig",
39
+ "SeeConfig",
40
+ "SeeBackendConfig",
41
+ "SeeBackendLocalConfig",
42
+ "SeeBackendGoogleCloudConfig",
43
+ "SeeBackendAzureConfig",
44
+ "ShineConfig",
45
+ "SpeakConfig",
46
+ "WaveConfig",
47
+ "STTBackendConfig",
48
+ "TTSBackendConfig",
49
+ ]
@@ -0,0 +1,251 @@
1
+ # Copyright 2026-present TJBot Contributors. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from typing import Any, Dict, List, Literal, Optional, Tuple
16
+
17
+ from pydantic import BaseModel, ConfigDict, Field
18
+ from ..utils.logging import TJBotLogLevel
19
+
20
+
21
+ class TJBotBaseModel(BaseModel):
22
+ model_config = ConfigDict(populate_by_name=True)
23
+
24
+
25
+ class LogConfig(TJBotBaseModel):
26
+ level: Optional[TJBotLogLevel] = "info"
27
+
28
+
29
+ class VADConfig(TJBotBaseModel):
30
+ enabled: Optional[bool] = True
31
+ model: Optional[str] = None
32
+ model_url: Optional[str] = Field(default=None, alias="modelUrl")
33
+
34
+
35
+ class STTBackendLocalConfig(TJBotBaseModel):
36
+ model: Optional[str] = None
37
+ model_url: Optional[str] = Field(default=None, alias="modelUrl")
38
+ vad: Optional[VADConfig] = None
39
+
40
+
41
+ class STTBackendIBMWatsonConfig(TJBotBaseModel):
42
+ model: Optional[str] = None
43
+ inactivity_timeout: Optional[int] = Field(default=None, alias="inactivityTimeout")
44
+ background_audio_suppression: Optional[float] = Field(
45
+ default=None, alias="backgroundAudioSuppression"
46
+ )
47
+ interim_results: Optional[bool] = Field(default=None, alias="interimResults")
48
+ credentials_path: Optional[str] = Field(default=None, alias="credentialsPath")
49
+
50
+
51
+ class STTBackendGoogleCloudConfig(TJBotBaseModel):
52
+ model: Optional[str] = None
53
+ language_code: Optional[str] = Field(default=None, alias="languageCode")
54
+ credentials_path: Optional[str] = Field(default=None, alias="credentialsPath")
55
+ encoding: Optional[str] = None
56
+ sample_rate_hertz: Optional[int] = Field(default=None, alias="sampleRateHertz")
57
+ audio_channel_count: Optional[int] = Field(default=None, alias="audioChannelCount")
58
+ enable_automatic_punctuation: Optional[bool] = Field(
59
+ default=None, alias="enableAutomaticPunctuation"
60
+ )
61
+ interim_results: Optional[bool] = Field(default=None, alias="interimResults")
62
+ region: Optional[str] = None
63
+ profanity_filter: Optional[bool] = Field(default=None, alias="profanityFilter")
64
+
65
+
66
+ class STTBackendAzureConfig(TJBotBaseModel):
67
+ language: Optional[str] = None
68
+ credentials_path: Optional[str] = Field(default=None, alias="credentialsPath")
69
+ interim_results: Optional[bool] = Field(default=None, alias="interimResults")
70
+
71
+
72
+ class STTBackendConfig(TJBotBaseModel):
73
+ type: Optional[
74
+ Literal["none", "local", "ibm-watson-stt", "google-cloud-stt", "azure-stt"]
75
+ ] = "local"
76
+ local: Optional[STTBackendLocalConfig] = None
77
+ ibm_watson_stt: Optional[STTBackendIBMWatsonConfig] = Field(
78
+ default=None, alias="ibm-watson-stt"
79
+ )
80
+ google_cloud_stt: Optional[STTBackendGoogleCloudConfig] = Field(
81
+ default=None, alias="google-cloud-stt"
82
+ )
83
+ azure_stt: Optional[STTBackendAzureConfig] = Field(default=None, alias="azure-stt")
84
+
85
+
86
+ class ListenConfig(TJBotBaseModel):
87
+ device: Optional[str] = None
88
+ microphone_rate: Optional[int] = Field(default=44100, alias="microphoneRate")
89
+ microphone_channels: Optional[int] = Field(default=2, alias="microphoneChannels")
90
+ model: Optional[str] = None
91
+ backend: Optional[STTBackendConfig] = None
92
+
93
+
94
+ class SeeBackendLocalConfig(TJBotBaseModel):
95
+ object_detection_model: Optional[str] = Field(
96
+ default=None, alias="objectDetectionModel"
97
+ )
98
+ image_classification_model: Optional[str] = Field(
99
+ default=None, alias="imageClassificationModel"
100
+ )
101
+ face_detection_model: Optional[str] = Field(
102
+ default=None, alias="faceDetectionModel"
103
+ )
104
+ object_detection_confidence: Optional[float] = Field(
105
+ default=None, alias="objectDetectionConfidence"
106
+ )
107
+ image_classification_confidence: Optional[float] = Field(
108
+ default=None, alias="imageClassificationConfidence"
109
+ )
110
+ face_detection_confidence: Optional[float] = Field(
111
+ default=None, alias="faceDetectionConfidence"
112
+ )
113
+
114
+
115
+ class SeeBackendGoogleCloudConfig(TJBotBaseModel):
116
+ credentials_path: Optional[str] = Field(default=None, alias="credentialsPath")
117
+ object_detection_confidence: Optional[float] = Field(
118
+ default=None, alias="objectDetectionConfidence"
119
+ )
120
+ image_classification_confidence: Optional[float] = Field(
121
+ default=None, alias="imageClassificationConfidence"
122
+ )
123
+ face_detection_confidence: Optional[float] = Field(
124
+ default=None, alias="faceDetectionConfidence"
125
+ )
126
+
127
+
128
+ class SeeBackendAzureConfig(TJBotBaseModel):
129
+ credentials_path: Optional[str] = Field(default=None, alias="credentialsPath")
130
+ object_detection_confidence: Optional[float] = Field(
131
+ default=None, alias="objectDetectionConfidence"
132
+ )
133
+ image_classification_confidence: Optional[float] = Field(
134
+ default=None, alias="imageClassificationConfidence"
135
+ )
136
+
137
+
138
+ class SeeBackendConfig(TJBotBaseModel):
139
+ type: Optional[Literal["none", "local", "google-cloud-vision", "azure-vision"]] = (
140
+ "none"
141
+ )
142
+ local: Optional[SeeBackendLocalConfig] = None
143
+ google_cloud_vision: Optional[SeeBackendGoogleCloudConfig] = Field(
144
+ default=None, alias="google-cloud-vision"
145
+ )
146
+ azure_vision: Optional[SeeBackendAzureConfig] = Field(
147
+ default=None, alias="azure-vision"
148
+ )
149
+
150
+
151
+ class SeeConfig(TJBotBaseModel):
152
+ camera_resolution: Optional[Tuple[int, int]] = Field(
153
+ default=(1920, 1080), alias="cameraResolution"
154
+ )
155
+ vertical_flip: Optional[bool] = Field(default=False, alias="verticalFlip")
156
+ horizontal_flip: Optional[bool] = Field(default=False, alias="horizontalFlip")
157
+ capture_timeout: Optional[int] = Field(default=None, alias="captureTimeout")
158
+ zero_shutter_lag: Optional[bool] = Field(default=None, alias="zeroShutterLag")
159
+ backend: Optional[SeeBackendConfig] = None
160
+
161
+
162
+ class LEDNeopixelConfig(TJBotBaseModel):
163
+ gpio_pin: Optional[int] = Field(default=None, alias="gpioPin")
164
+ spi_interface: Optional[str] = Field(default=None, alias="spiInterface")
165
+ use_grb_format: Optional[bool] = Field(default=False, alias="useGRBFormat")
166
+
167
+
168
+ class LEDCommonAnodeConfig(TJBotBaseModel):
169
+ red_pin: Optional[int] = Field(default=None, alias="redPin")
170
+ green_pin: Optional[int] = Field(default=None, alias="greenPin")
171
+ blue_pin: Optional[int] = Field(default=None, alias="bluePin")
172
+
173
+
174
+ class ShineConfig(TJBotBaseModel):
175
+ has_neopixel_led: Optional[bool] = Field(default=False, alias="hasNeopixelLED")
176
+ has_common_anode_led: Optional[bool] = Field(
177
+ default=False, alias="hasCommonAnodeLED"
178
+ )
179
+ neopixel: Optional[LEDNeopixelConfig] = None
180
+ common_anode: Optional[LEDCommonAnodeConfig] = Field(
181
+ default=None, alias="commonanode"
182
+ )
183
+
184
+
185
+ class TTSBackendLocalConfig(TJBotBaseModel):
186
+ model: Optional[str] = None
187
+ model_url: Optional[str] = Field(default=None, alias="modelUrl")
188
+
189
+
190
+ class TTSBackendIBMWatsonConfig(TJBotBaseModel):
191
+ voice: Optional[str] = None
192
+ credentials_path: Optional[str] = Field(default=None, alias="credentialsPath")
193
+
194
+
195
+ class TTSBackendGoogleCloudConfig(TJBotBaseModel):
196
+ voice: Optional[str] = None
197
+ language_code: Optional[str] = Field(default=None, alias="languageCode")
198
+ credentials_path: Optional[str] = Field(default=None, alias="credentialsPath")
199
+
200
+
201
+ class TTSBackendAzureConfig(TJBotBaseModel):
202
+ voice: Optional[str] = None
203
+ credentials_path: Optional[str] = Field(default=None, alias="credentialsPath")
204
+
205
+
206
+ class TTSBackendConfig(TJBotBaseModel):
207
+ type: Optional[
208
+ Literal["none", "local", "ibm-watson-tts", "google-cloud-tts", "azure-tts"]
209
+ ] = "local"
210
+ local: Optional[TTSBackendLocalConfig] = None
211
+ ibm_watson_tts: Optional[TTSBackendIBMWatsonConfig] = Field(
212
+ default=None, alias="ibm-watson-tts"
213
+ )
214
+ google_cloud_tts: Optional[TTSBackendGoogleCloudConfig] = Field(
215
+ default=None, alias="google-cloud-tts"
216
+ )
217
+ azure_tts: Optional[TTSBackendAzureConfig] = Field(default=None, alias="azure-tts")
218
+
219
+
220
+ class SpeakConfig(TJBotBaseModel):
221
+ device: Optional[str] = None
222
+ backend: Optional[TTSBackendConfig] = None
223
+
224
+
225
+ class WaveConfig(TJBotBaseModel):
226
+ gpio_chip: Optional[int] = Field(default=0, alias="gpioChip")
227
+ servo_pin: Optional[int] = Field(default=18, alias="servoPin")
228
+
229
+
230
+ class HardwareConfig(TJBotBaseModel):
231
+ speaker: Optional[bool] = False
232
+ microphone: Optional[bool] = False
233
+ led: Optional[bool] = False
234
+ servo: Optional[bool] = False
235
+ camera: Optional[bool] = False
236
+
237
+
238
+ STTEngineConfig = Dict[str, Any]
239
+ TTSEngineConfig = Dict[str, Any]
240
+
241
+
242
+ class TJBotConfigSchema(TJBotBaseModel):
243
+ log: Optional[LogConfig] = Field(default_factory=LogConfig)
244
+ hardware: Optional[HardwareConfig] = Field(default_factory=HardwareConfig)
245
+ listen: Optional[ListenConfig] = Field(default_factory=ListenConfig)
246
+ see: Optional[SeeConfig] = Field(default_factory=SeeConfig)
247
+ shine: Optional[ShineConfig] = Field(default_factory=ShineConfig)
248
+ speak: Optional[SpeakConfig] = Field(default_factory=SpeakConfig)
249
+ wave: Optional[WaveConfig] = Field(default_factory=WaveConfig)
250
+ recipe: Optional[Dict[str, Any]] = Field(default_factory=dict)
251
+ models: Optional[List[Dict[str, Any]]] = Field(default_factory=list)