hackerbot 0.5.0__py3-none-any.whl → 0.6.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.
@@ -16,8 +16,12 @@
16
16
 
17
17
 
18
18
  from hackerbot.utils.hackerbot_helper import HackerbotHelper
19
+ from hackerbot.utils.tts_helper import TTSHelper
19
20
  from .maps import Maps
20
21
  import time
22
+ import sounddevice as sd
23
+ from piper.voice import PiperVoice
24
+ import numpy as np
21
25
 
22
26
  class Base():
23
27
  def __init__(self, controller: HackerbotHelper):
@@ -220,4 +224,67 @@ class Base():
220
224
  if auto_dock:
221
225
  time.sleep(3.0)
222
226
  self.dock(block=False)
223
- self._controller.destroy()
227
+ self._controller.destroy()
228
+
229
+ def speak(self, model_src, text, speaker_id=None):
230
+ """
231
+ Synthesize and play speech audio based on the given text and voice model.
232
+
233
+ This function attempts to load a voice model, initialize an audio stream,
234
+ synthesize the given text into audio, and play it through the audio output
235
+ stream. If any step fails, an error is logged, and the process is aborted.
236
+
237
+ Args:
238
+ model_src: The source of the voice model to load for speech synthesis.
239
+ text: The text content to be synthesized into speech.
240
+ speaker_id (optional): The ID of the speaker to use, if applicable.
241
+
242
+ Returns:
243
+ None
244
+ """
245
+ try:
246
+ try:
247
+ tts_helper = TTSHelper()
248
+ model_path = tts_helper.get_or_download_model(model_src)
249
+ except Exception as e:
250
+ self._controller.log_error(f"Failed to get or download model: {e}")
251
+ return
252
+
253
+ try:
254
+ voice = PiperVoice.load(model_path)
255
+ except Exception as e:
256
+ self._controller.log_error(f"Failed to load voice model: {e}")
257
+ return
258
+
259
+ try:
260
+ stream = sd.OutputStream(
261
+ samplerate=voice.config.sample_rate,
262
+ channels=1,
263
+ dtype='int16',
264
+ blocksize=0 # Let sounddevice choose blocksize automatically
265
+ )
266
+ except Exception as e:
267
+ self._controller.log_error(f"Failed to initialize audio stream: {e}")
268
+ return
269
+
270
+ try:
271
+ with stream:
272
+ for audio_bytes in voice.synthesize_stream_raw(text, speaker_id=speaker_id):
273
+ try:
274
+ int_data = np.frombuffer(audio_bytes, dtype=np.int16)
275
+ stream.write(int_data)
276
+ except Exception as e:
277
+ self._controller.log_error(f"Error writing audio data to stream: {e}")
278
+ break
279
+
280
+ try:
281
+ stream.stop()
282
+ except Exception as e:
283
+ self._controller.log_error(f"Failed to stop audio stream cleanly: {e}")
284
+
285
+ print("Finished speaking.")
286
+
287
+ except Exception as e:
288
+ self._controller.log_error(f"Error during audio streaming: {e}")
289
+ except Exception as e:
290
+ self._controller.log_error(f"Error in base:speak: {e}")
@@ -17,9 +17,6 @@
17
17
 
18
18
  from hackerbot.utils.hackerbot_helper import HackerbotHelper
19
19
  from .eyes import Eyes
20
- import sounddevice as sd
21
- from piper.voice import PiperVoice
22
- import numpy as np
23
20
 
24
21
  class Head():
25
22
  def __init__(self, controller: HackerbotHelper):
@@ -62,26 +59,3 @@ class Head():
62
59
  except Exception as e:
63
60
  self._controller.log_error(f"Error in head:set_idle_mode: {e}")
64
61
  return False
65
-
66
- def speak(self, model_src, text, speaker_id = None):
67
- model = model_src
68
- voice = PiperVoice.load(model)
69
-
70
- # Setup a sounddevice OutputStream with appropriate parameters
71
- # The sample rate and channels should match the properties of the PCM data
72
- stream = sd.OutputStream(
73
- samplerate=voice.config.sample_rate,
74
- channels=1,
75
- dtype='int16',
76
- blocksize=0 # Let sounddevice choose blocksize automatically
77
- )
78
-
79
- with stream: # This automatically handles start/stop/close
80
- for audio_bytes in voice.synthesize_stream_raw(text, speaker_id=speaker_id):
81
- int_data = np.frombuffer(audio_bytes, dtype=np.int16)
82
- stream.write(int_data)
83
-
84
- # At this point, all data has been written,
85
- # but we need to wait for any remaining buffered audio to play out.
86
- stream.stop() # Ensure stream stops cleanly
87
- print("Finished speaking.") # <-- You now know it's done
@@ -0,0 +1,101 @@
1
+ ################################################################################
2
+ # Copyright (c) 2025 Hackerbot Industries LLC
3
+ #
4
+ # This source code is licensed under the MIT license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+ #
7
+ # Created By: Allen Chien
8
+ # Created: April 2025
9
+ # Updated: 2025.05.13
10
+ #
11
+ # This module contains the TTSHelper class that gets or downloads a Piper voice
12
+ # model from HuggingFace
13
+ #
14
+ # Special thanks to the following for their code contributions to this codebase:
15
+ # Allen Chien - https://github.com/AllenChienXXX
16
+ # Saad Elbeleidy - https://github.com/sbeleidy
17
+ #
18
+ # Credits:
19
+ # This project makes use of functionality inspired by or adapted from:
20
+ # Dimits - https://github.com/Reqeique/Dimits
21
+ ################################################################################
22
+
23
+
24
+ import os
25
+ from huggingface_hub import hf_hub_url
26
+ import requests
27
+
28
+ class TTSHelper:
29
+ """
30
+ Downloads a Piper voice model (.onnx + .json) from HuggingFace
31
+ and stores it in a local directory for future use.
32
+ """
33
+
34
+ def __init__(self, cache_dir: str = os.path.expanduser("~/piper_models")):
35
+ """
36
+ Args:
37
+ cache_dir (str): Directory where downloaded models will be stored. Defaults to "~/piper_models".
38
+ """
39
+ self.cache_dir = cache_dir
40
+ os.makedirs(self.cache_dir, exist_ok=True)
41
+
42
+ def get_or_download_model(self, model_src: str) -> str:
43
+ """
44
+ If model_src is a valid file path, returns it.
45
+ Otherwise treats it as a HuggingFace voice model name and downloads it.
46
+
47
+ Args:
48
+ model_src (str): Either local .onnx path or voice name (e.g., "en_US-amy-low").
49
+
50
+ Returns:
51
+ str: Path to ONNX model file.
52
+ """
53
+ if os.path.isfile(model_src):
54
+ return model_src
55
+ return self._download_model_from_huggingface(model_src)
56
+
57
+ def _download_model_from_huggingface(self, voice: str) -> str:
58
+ """
59
+ Downloads a Piper voice model (.onnx and .json) from HuggingFace.
60
+
61
+ Args:
62
+ voice (str): Voice model name, e.g., "en_US-amy-low".
63
+
64
+ Returns:
65
+ str: Path to downloaded .onnx model.
66
+
67
+ Raises:
68
+ RuntimeError: If the download fails.
69
+ """
70
+ try:
71
+ locale, person, pitch = voice.split('-')
72
+ lang, country = locale.split('_')
73
+ except ValueError:
74
+ raise RuntimeError(f"Invalid voice model name: {voice}")
75
+
76
+ base_path = f"{lang}/{locale}/{person}/{pitch}"
77
+ filename_onnx = f"{voice}.onnx"
78
+ filename_json = f"{voice}.onnx.json"
79
+
80
+ url_onnx = hf_hub_url(repo_id="rhasspy/piper-voices", filename=f"{base_path}/{filename_onnx}")
81
+ url_json = hf_hub_url(repo_id="rhasspy/piper-voices", filename=f"{base_path}/{filename_json}")
82
+
83
+ path_onnx = os.path.join(self.cache_dir, filename_onnx)
84
+ path_json = os.path.join(self.cache_dir, filename_json)
85
+
86
+ if not os.path.exists(path_onnx):
87
+ self._download_file(url_onnx, path_onnx)
88
+ if not os.path.exists(path_json):
89
+ self._download_file(url_json, path_json)
90
+
91
+ return path_onnx
92
+
93
+ def _download_file(self, url: str, dest_path: str):
94
+ try:
95
+ response = requests.get(url, stream=True)
96
+ response.raise_for_status()
97
+ with open(dest_path, 'wb') as f:
98
+ for chunk in response.iter_content(chunk_size=8192):
99
+ f.write(chunk)
100
+ except Exception as e:
101
+ raise RuntimeError(f"Failed to download {url}: {e}")
@@ -0,0 +1,129 @@
1
+ Metadata-Version: 2.4
2
+ Name: hackerbot
3
+ Version: 0.6.0
4
+ Summary: This module contains the setup for the hackerbot python package.
5
+ Author-email: Allen Chien <allen@hackerbot.co>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/hackerbotindustries/hackerbot-python-package
8
+ Requires-Python: >=3.11
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: pyserial
12
+ Requires-Dist: pytest
13
+ Requires-Dist: piper-tts
14
+ Requires-Dist: sounddevice
15
+ Requires-Dist: pyaudio
16
+ Requires-Dist: huggingface_hub
17
+ Requires-Dist: requests
18
+ Dynamic: license-file
19
+
20
+ ![HackerBot](images/transparent_hb_horizontal_industries_.png)
21
+ # Hackerbot Python Package
22
+
23
+ The `hackerbot` Python package provides modules for controlling and managing the Hackerbot robotics system.
24
+
25
+ ## Installation
26
+
27
+ You can now install the package directly from PyPI:
28
+
29
+ ```bash
30
+ pip install hackerbot
31
+ ```
32
+
33
+ This will automatically install all required dependencies and make the `hackerbot` package available in your Python environment.
34
+
35
+ ### (Optional) Installing from Source
36
+
37
+ If you prefer to install from source for development purposes:
38
+
39
+ 1. **Clone the Repository**
40
+
41
+ ```bash
42
+ git clone https://github.com/hackerbotindustries/hackerbot-python-package.git
43
+ cd hackerbot-python-package/
44
+ ```
45
+
46
+ 2. **Install Locally**
47
+
48
+ ```bash
49
+ pip install .
50
+ ```
51
+
52
+ ## Quick Start
53
+
54
+ SSH into the Raspberry Pi or open a VNC viewer, then install the official Python package:
55
+
56
+ ```bash
57
+ pip install hackerbot
58
+ ```
59
+
60
+ Or upgrade the existing package:
61
+
62
+ ```bash
63
+ pip install --upgrade hackerbot
64
+ ```
65
+
66
+ Then, run `python3` to open up the Python interactive shell and copy and paste the following:
67
+
68
+ ```python
69
+ from hackerbot import Hackerbot
70
+
71
+ bot = Hackerbot()
72
+
73
+ bot.base.drive(0, 65)
74
+ bot.base.drive(200, 0)
75
+ ```
76
+
77
+ You should see your Hackerbot leave the charger and move in the opposite direction.
78
+
79
+ ```python
80
+ bot.head.look(180, 250, 70)
81
+ ```
82
+
83
+ Now your robot should move its head and look up at you!
84
+
85
+ ```python
86
+ bot.arm.move_joints(0, 0, 0, 0, 0, 0, 10)
87
+ ```
88
+
89
+ You should see your elephant arm moving to a straight-up position.
90
+
91
+ ```python
92
+ bot.base.destroy(auto_dock=True)
93
+ ```
94
+
95
+ Safely clean up, and your Hackerbot will return to the charger. Once `destroy` is called, you need to create a new Hackerbot instance to perform new actions.
96
+
97
+ ## Usage
98
+
99
+ After installation, you can import and use the package in your Python scripts:
100
+
101
+ ```python
102
+ import hackerbot
103
+ ```
104
+
105
+ ## Testing
106
+
107
+ To run the unit tests:
108
+
109
+ ```bash
110
+ cd tests/unit_tests
111
+ pytest
112
+ ```
113
+
114
+ ## Troubleshooting
115
+
116
+ If you run into issues during installation or usage, try the following:
117
+
118
+ * Use a virtual environment:
119
+
120
+ ```bash
121
+ python3 -m venv venv
122
+ source venv/bin/activate
123
+ ```
124
+
125
+ * Upgrade `pip`:
126
+
127
+ ```bash
128
+ pip install --upgrade pip
129
+ ```
@@ -2,14 +2,15 @@ hackerbot/__init__.py,sha256=fjzBB_VsqCuKJ3yeLdqLSvAQIz9bAMI-KXrUC6ZrscY,1248
2
2
  hackerbot/core.py,sha256=oZKQEUaMn19MnaWNTDFqTZ7lziBEZeSk5CdLXLSLvr0,5521
3
3
  hackerbot/arm/__init__.py,sha256=JdCmTDQ2qk3W8-Alc4mjaQYxHnmH26r0nSy0JFg6J4A,3400
4
4
  hackerbot/arm/gripper.py,sha256=xZux0PZL4vvrJTTKHL7dDsRBfJr4zbmgMaA0uBR1u4s,1986
5
- hackerbot/base/__init__.py,sha256=PG2Ykn2MLfobAWClGFnp-AZfh1xVsnbc1nQ8E6Yy6eA,8698
5
+ hackerbot/base/__init__.py,sha256=7OtK29R8G9JYkg_hYWJbZ4DhX0PtldYCneyrZE31qCQ,11387
6
6
  hackerbot/base/maps.py,sha256=I_4NdvOMnkHX6-mW43QpDPJ-eVc6npgcBXq99tU7D1g,5916
7
- hackerbot/head/__init__.py,sha256=w4mhnz70miLaChxxF-8bGntDRo8GFLTFICxtTWJX2Iw,3581
7
+ hackerbot/head/__init__.py,sha256=uBJ4gTst_QuCdveeNnuYZLIwsS8ANK9hxKMsLJVpI74,2467
8
8
  hackerbot/head/eyes.py,sha256=xqeKMxL12iaa8KQzDlbgbNy3LzcmWm8aXkebztYJ4P8,1370
9
9
  hackerbot/utils/hackerbot_helper.py,sha256=egQPVBBUo52ywsO6jGAGVqhyGLpMPz-b2fgLBb39WSM,4998
10
10
  hackerbot/utils/serial_helper.py,sha256=qo-mpbE_sb23_IAIfDuRT8sCDtW8d8A8qyav-b9e5Kw,6567
11
- hackerbot-0.5.0.dist-info/licenses/LICENSE,sha256=SCcXH0bf35ISRu_Ks8xEKySHXiqclANLWhBUbVU8VvA,1081
12
- hackerbot-0.5.0.dist-info/METADATA,sha256=TjRFtJ_ZSFGW50WkBhwEHlQfNkOXSJWd5wXZgasXoJ0,1786
13
- hackerbot-0.5.0.dist-info/WHEEL,sha256=ck4Vq1_RXyvS4Jt6SI0Vz6fyVs4GWg7AINwpsaGEgPE,91
14
- hackerbot-0.5.0.dist-info/top_level.txt,sha256=2n_FStAr1SiI3BV67x7KJHYIOmEwxjUD59zedw2hLkU,10
15
- hackerbot-0.5.0.dist-info/RECORD,,
11
+ hackerbot/utils/tts_helper.py,sha256=X-3G2hhq94i9z3MdLzD-pFSwnIW707pI7Leu1h8EO60,3575
12
+ hackerbot-0.6.0.dist-info/licenses/LICENSE,sha256=SCcXH0bf35ISRu_Ks8xEKySHXiqclANLWhBUbVU8VvA,1081
13
+ hackerbot-0.6.0.dist-info/METADATA,sha256=V95jUbd4OUR80K9xnCg2uIjeCmMpAC2NmgSW4PHx8GM,2724
14
+ hackerbot-0.6.0.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
15
+ hackerbot-0.6.0.dist-info/top_level.txt,sha256=2n_FStAr1SiI3BV67x7KJHYIOmEwxjUD59zedw2hLkU,10
16
+ hackerbot-0.6.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.0.0)
2
+ Generator: setuptools (80.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,69 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: hackerbot
3
- Version: 0.5.0
4
- Summary: This module contains the setup for the hackerbot python package.
5
- Author-email: Allen Chien <allen@hackerbot.co>
6
- License-Expression: MIT
7
- Project-URL: Homepage, https://github.com/hackerbotindustries/hackerbot-python-package
8
- Requires-Python: >=3.11
9
- Description-Content-Type: text/markdown
10
- License-File: LICENSE
11
- Requires-Dist: pyserial
12
- Requires-Dist: pytest
13
- Requires-Dist: piper-tts
14
- Requires-Dist: sounddevice
15
- Requires-Dist: pyaudio
16
- Dynamic: license-file
17
-
18
- # Hackerbot Python Package
19
-
20
- Hackerbot python package (`hackerbot-python-package`) is a project that includes modules for controlling and managing the Hackerbot system.
21
-
22
- ## Installation
23
-
24
- Follow these steps to clone the repository and set up the required dependencies.
25
-
26
- ### 1. Clone the Repository
27
- Use SSH to clone the repository:
28
- ```bash
29
- https://github.com/hackerbotindustries/hackerbot-python-package.git
30
- ```
31
- This will create a directory named `hackerbot-python-package` and download all necessary files.
32
-
33
- ### 2. Navigate to the Modules Directory
34
- Move into the `hackerbot_modules` directory:
35
- ```bash
36
- cd hackerbot-python-package/
37
- ```
38
-
39
- ### 3. Install Dependencies
40
- Install the `hackerbot` package using `pip`:
41
- ```bash
42
- pip install .
43
- ```
44
- This will install the package locally for your Python environment.
45
-
46
- ## Usage
47
- Once installed, you can import `hackerbot` in your Python scripts:
48
- ```python
49
- import hackerbot
50
- ```
51
-
52
- ### 4. Testing
53
- To run the unit tests run:
54
- ```bash
55
- cd tests/unit_tests
56
- pytest
57
- ```
58
-
59
- ## Troubleshooting
60
- If you run into issues with the installation, try the following:
61
- - Ensure you're using a virtual environment:
62
- ```bash
63
- python3 -m venv venv
64
- source venv/bin/activate
65
- ```
66
- - Upgrade `pip` before installation:
67
- ```bash
68
- pip install --upgrade pip
69
- ```