· 5 years ago · Dec 12, 2019, 06:10 PM
1# Jarbas - utils
2
3collection of simple utilities for use across the mycroft ecosystem
4
5* [Install](#install)
6* [Usage](#usage)
7 + [Messagebus](#messagebus)
8 + [Configuration](#configuration)
9 - [Wake words](#wake-words)
10 + [Enclosures](#enclosures)
11 - [System actions](#system-actions)
12 - [Sound](#sound)
13 + [Data](#data)
14
15
16## Install
17
18stable version on pip 0.3.0
19
20```bash
21pip install jarbas_utils
22```
23
24dev version (this branch) - 0.3.x - 0.4
25
26```bash
27pip install git+https://github.com/JarbasAl/jarbas_utils
28```
29
30## Usage
31
32### Messagebus
33
34The main way to interact with a mycroft instance is using the messagebus
35
36 WARNING: the mycroft bus is unencrypted, be sure to secure your communications in some way before you start poking firewall ports open
37
38Listening for events is super easy, here is a small program counting number of spoken utterances
39
40```python
41from jarbas_utils.messagebus import listen_for_message
42from jarbas_utils.log import LOG
43from jarbas_utils import wait_for_exit_signal
44
45spoken = 0
46
47
48def handle_speak(message):
49 global spoken
50 spoken += 1
51 LOG.info("Mycroft spoke {n} sentences since start".format(n=spoken))
52
53
54bus = listen_for_message("speak", handle_speak)
55wait_for_exit_signal() # wait for ctrl+c
56
57# cleanup is a good practice!
58bus.remove_all_listeners("speak")
59bus.close()
60```
61
62Triggering events in mycroft is also trivial
63
64```python
65from jarbas_utils.messagebus import send_message
66from jarbas_utils.log import LOG
67from jarbas_utils import create_daemon, wait_for_exit_signal
68import random
69from time import sleep
70
71
72def alert():
73 LOG.info("Alerting user of some event using Mycroft")
74 send_message("speak", {"utterance": "Alert! something happened"})
75
76
77def did_something_happen():
78 while True:
79 if random.choice([True, False]):
80 alert()
81 sleep(10)
82
83
84create_daemon(did_something_happen) # check for something in background
85wait_for_exit_signal() # wait for ctrl+c
86```
87
88You can also connect to a remote messagebus, here is a live translator using language utils
89
90```python
91from jarbas_utils.messagebus import get_mycroft_bus, listen_for_message
92from jarbas_utils import wait_for_exit_signal
93from jarbas_utils.lang.translate import say_in_language
94
95
96bus_ip = "0.0.0.0" # enter a remote ip here, remember bus is unencrypted! careful with opening firewalls
97bus = get_mycroft_bus(host=bus_ip)
98
99
100TARGET_LANG = "pt"
101
102
103def translate(message):
104 utterance = message.data["utterance"]
105 say_in_language(utterance, lang=TARGET_LANG)
106
107
108listen_for_message("speak", translate, bus=bus)
109
110wait_for_exit_signal() # wait for ctrl+c
111
112bus.remove_all_listeners("speak")
113bus.close()
114```
115
116### Configuration
117
118utils are provided to manipulate the user config
119
120NOTE: this assumes you are running this code on the same machine as mycroft, it manipulates files directly in your system
121
122```python
123from jarbas_utils.configuration import read_mycroft_config
124
125config = read_mycroft_config()
126stt = config["stt"]["module"]
127```
128
129individual configs can also be manipulated
130```python
131from jarbas_utils.configuration import MycroftUserConfig, MycroftSystemConfig, MycroftDefaultConfig
132from jarbas_utils.log import LOG
133
134config = MycroftUserConfig()
135config["lang"] = "pt"
136config["tts"] = {"module": "google"}
137config.store()
138
139config = MycroftSystemConfig(allow_overwrite=True)
140config["enclosure"] = {"platform": "respeaker"}
141config.store()
142
143config = MycroftDefaultConfig()
144config.set_mycroft_root("~/PycharmProjects/mycroft-core") # not needed for mark1/mark2/picroft
145lang = config["lang"]
146try:
147 config["lang"] = "pt"
148except PermissionError:
149 LOG.error("config is read only")
150```
151
152you can also use the LocalConf class with your own path for other use cases
153```python
154from jarbas_utils.configuration import LocalConf
155
156MY_CONFIG = "~/.projectX/projectX.conf"
157
158class MyConfig(LocalConf):
159 def __init__(self):
160 super().__init__(MY_CONFIG)
161
162config = MyConfig()
163if not config.get("lang"):
164 config["lang"] = "pt"
165 config.store() # now changes are saved
166
167config.merge({"host": "http://somedomain.net"})
168config.reload() # now changes are gone
169
170```
171
172#### Wake words
173
174when defining pocketsphinx wake words you often need to know the phonemes
175
176```python
177from jarbas_utils.configuration import update_mycroft_config
178from jarbas_utils.lang.phonemes import get_phonemes
179
180
181def create_wakeword(word, sensitivity):
182 # sensitivity is a bitch to do automatically
183 # IDEA make some web ui or whatever to tweak it experimentally
184 phonemes = get_phonemes(word)
185 config = {
186 "listener": {
187 "wake_word": word
188 },
189 word: {
190 "andromeda": {
191 "module": "pocketsphinx",
192 "phonemes": phonemes,
193 "sample_rate": 16000,
194 "threshold": sensitivity,
195 "lang": "en-us"
196 }
197 }
198 }
199 update_mycroft_config(config)
200
201create_wakeword("andromeda", "1e-25")
202
203```
204
205Here is some sample output from get_phonemes
206
207 hey mycroft HH EY1 . M Y K R OW F T
208 hey chatterbox HH EY1 . CH AE T EH R B OW K S
209 alexa AH0 L EH1 K S AH0
210
211
212### Enclosures
213
214If you are making a system enclosure you will likely need to handle system actions
215
216#### System actions
217
218```python
219from jarbas_utils.system import system_reboot, system_shutdown, ssh_enable, ssh_disable
220from jarbas_utils.log import LOG
221from jarbas_utils.messagebus import get_mycroft_bus, Message
222
223
224class MyEnclosureClass:
225
226 def __init__(self):
227 LOG.info('Setting up client to connect to a local mycroft instance')
228 self.bus = get_mycroft_bus()
229 self.bus.on("system.reboot", self.handle_reboot)
230
231 def speak(self, utterance):
232 LOG.info('Sending speak message...')
233 self.bus.emit(Message('speak', data={'utterance': utterance}))
234
235 def handle_reboot(self, message):
236 self.speak("rebooting")
237 system_reboot()
238
239
240```
241#### Sound
242
243Volume control is also a common thing you need to handle
244
245```python
246from jarbas_utils.sound.alsa import AlsaControl
247#from jarbas_utils.sound.pulse import PulseAudio
248from jarbas_utils.log import LOG
249from jarbas_utils.messagebus import get_mycroft_bus, Message
250
251
252class MyPretendEnclosure:
253
254 def __init__(self):
255 LOG.info('Setting up client to connect to a local mycroft instance')
256 self.bus = get_mycroft_bus()
257 # None of these are mycroft signals, but you get the point
258 self.bus.on("set.volume", self.handle_set_volume)
259 self.bus.on("speak.volume", self.handle_speak_volume)
260 self.bus.on("mute.volume", self.handle_mute_volume)
261 self.bus.on("unmute.volume", self.handle_unmute_volume)
262
263 self.alsa = AlsaControl()
264 # self.pulse = PulseAudio()
265
266 def speak(self, utterance):
267 self.bus.emit(Message('speak', data={'utterance': utterance}))
268
269 def handle_speak_volume(self, message):
270 volume = self.alsa.get_volume()
271 self.speak(volume)
272
273 def handle_mute_volume(self, message):
274 if not self.alsa.is_muted():
275 self.alsa.mute()
276
277 def handle_unmute_volume(self, message):
278 if self.alsa.is_muted():
279 self.alsa.unmute()
280
281 def handle_set_volume(self, message):
282 volume = message.data.get("volume", 50)
283 assert 0 <= volume <= 100
284 self.alsa.set_volume(volume)
285
286```
287
288## Data
289
290searching and storing data is a common operation that can be tricky
291
292i provide a simple class to handle arbitrary json serializable data
293
294```python
295from jarbas_utils.database import JsonDatabase
296
297db = JsonDatabase("users", "~/databases/users.json")
298
299class User:
300 def __init__(self, email, key=None, data=None):
301 self.email = email
302 self.secret_key = key
303 self.data = data
304
305
306user1 = User("something@mail.net",
307 data={"name": "jonas", "birthday": "12 May"})
308user2 = User("second@mail.net", "secret",
309 data={"name": ["joe", "jony"], "age": 12})
310
311db.add_item(user1)
312db.add_item(user2)
313
314# search entries with non empty secret_key
315print(db.search_by_key("secret_key", include_empty=False))
316
317# search in user provided data
318print(db.search_by_key("birth", fuzzy=True))
319
320# search entries with a certain value
321print(db.search_by_value("age", 12))
322print(db.search_by_value("name", "jon", fuzzy=True))
323
324db.commit() # save
325```