15 from __future__
import absolute_import
21 from os.path
import join, expanduser, splitext
23 from threading
import Thread
24 from time
import sleep
29 from stat
import S_ISREG, ST_MTIME, ST_MODE, ST_SIZE
47 """Convert a resource into an absolute filename. 49 Resource names are in the form: 'filename.ext' 50 or 'path/filename.ext' 52 The system wil look for ~/.mycroft/res_name first, and 53 if not found will look at /opt/mycroft/res_name, 54 then finally it will look for res_name in the 'mycroft/res' 55 folder of the source code package. 58 With mycroft running as the user 'bob', if you called 59 resolve_resource_file('snd/beep.wav') 60 it would return either '/home/bob/.mycroft/snd/beep.wav' or 61 '/opt/mycroft/snd/beep.wav' or '.../mycroft/res/snd/beep.wav', 62 where the '...' is replaced by the path where the package has 66 res_name (str): a resource path/name 68 str: path to resource or None if no resource found 70 config = mycroft.configuration.Configuration.get()
73 if os.path.isfile(res_name):
77 filename = os.path.expanduser(
"~/.mycroft/" + res_name)
78 if os.path.isfile(filename):
82 data_dir = expanduser(config[
'data_dir'])
83 filename = os.path.expanduser(join(data_dir, res_name))
84 if os.path.isfile(filename):
88 filename = os.path.join(os.path.dirname(__file__),
'..',
'res', res_name)
89 filename = os.path.abspath(os.path.normpath(filename))
90 if os.path.isfile(filename):
97 """ Play an audio file. 99 This wraps the other play_* functions, choosing the correct one based on 100 the file extension. The function will return directly and play the file 106 Returns: subprocess.Popen object. None if the format is not supported or 107 an error occurs playing the file. 110 extension_to_function = {
115 _, extension = splitext(uri)
116 play_function = extension_to_function.get(extension.lower())
118 return play_function(uri)
120 LOG.error(
"Could not find a function capable of playing {uri}." 121 " Supported formats are {keys}." 122 .format(uri=uri, keys=list(extension_to_function.keys())))
129 This will use the application specified in the mycroft config 130 and play the uri passed as argument. The function will return directly 131 and play the file in the background. 136 Returns: subprocess.Popen object 138 config = mycroft.configuration.Configuration.get()
139 play_cmd = config.get(
"play_wav_cmdline")
140 play_wav_cmd = str(play_cmd).split(
" ")
141 for index, cmd
in enumerate(play_wav_cmd):
143 play_wav_cmd[index] = (
get_http(uri))
145 return subprocess.Popen(play_wav_cmd)
146 except Exception
as e:
147 LOG.error(
"Failed to launch WAV: {}".format(play_wav_cmd))
148 LOG.debug(
"Error: {}".format(repr(e)), exc_info=
True)
155 This will use the application specified in the mycroft config 156 and play the uri passed as argument. The function will return directly 157 and play the file in the background. 162 Returns: subprocess.Popen object 164 config = mycroft.configuration.Configuration.get()
165 play_cmd = config.get(
"play_mp3_cmdline")
166 play_mp3_cmd = str(play_cmd).split(
" ")
167 for index, cmd
in enumerate(play_mp3_cmd):
169 play_mp3_cmd[index] = (
get_http(uri))
171 return subprocess.Popen(play_mp3_cmd)
172 except Exception
as e:
173 LOG.error(
"Failed to launch MP3: {}".format(play_mp3_cmd))
174 LOG.debug(
"Error: {}".format(repr(e)), exc_info=
True)
181 This will use the application specified in the mycroft config 182 and play the uri passed as argument. The function will return directly 183 and play the file in the background. 188 Returns: subprocess.Popen object 190 config = mycroft.configuration.Configuration.get()
191 play_cmd = config.get(
"play_ogg_cmdline")
192 play_ogg_cmd = str(play_cmd).split(
" ")
193 for index, cmd
in enumerate(play_ogg_cmd):
195 play_ogg_cmd[index] = (
get_http(uri))
197 return subprocess.Popen(play_ogg_cmd)
198 except Exception
as e:
199 LOG.error(
"Failed to launch OGG: {}".format(play_ogg_cmd))
200 LOG.debug(
"Error: {}".format(repr(e)), exc_info=
True)
204 def record(file_path, duration, rate, channels):
206 return subprocess.Popen(
207 [
"arecord",
"-r", str(rate),
"-c", str(channels),
"-d",
208 str(duration), file_path])
210 return subprocess.Popen(
211 [
"arecord",
"-r", str(rate),
"-c", str(channels), file_path])
215 """ Find audio input device by name. 218 device_name: device name or regex pattern to match 220 Returns: device_index (int) or None if device wasn't found 222 LOG.info(
'Searching for input device: {}'.format(device_name))
223 LOG.debug(
'Devices: ')
224 pa = pyaudio.PyAudio()
225 pattern = re.compile(device_name)
226 for device_index
in range(pa.get_device_count()):
227 dev = pa.get_device_info_by_index(device_index)
228 LOG.debug(
' {}'.format(dev[
'name']))
229 if dev[
'maxInputChannels'] > 0
and pattern.match(dev[
'name']):
230 LOG.debug(
' ^-- matched')
236 return uri.replace(
"https://",
"http://")
240 if url
and url.endswith(
'/'):
246 with open(filename,
'r') as f: 247 return [line.strip()
for line
in f]
252 with open(filename,
'r') as f: 254 (key, val) = line.split(div)
255 d[key.strip()] = val.strip()
260 """ Check connection by connecting to 8.8.8.8, if this is 261 blocked/fails, Microsoft NCSI is used as a backup 264 True if internet connection can be detected 270 """ Check internet connection by retrieving the Microsoft NCSI endpoint. 273 True if internet connection can be detected 276 r = requests.get(
'http://www.msftncsi.com/ncsi.txt')
277 if r.text ==
u'Microsoft NCSI':
285 """ Check internet connection by connecting to DNS servers 288 True if internet connection can be detected 295 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
296 s.settimeout(timeout)
297 s.connect((host, port))
301 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
302 s.settimeout(timeout)
303 s.connect((
"8.8.4.4", port))
310 """Clear out the directory if needed 312 This assumes all the files in the directory can be deleted as freely 315 directory (str): directory path that holds cached files 316 min_free_percent (float): percentage (0.0-100.0) of drive to keep free, 317 default is 5% if not specified. 318 min_free_disk (float): minimum allowed disk space in MB, default 319 value is 50 MB if not specified. 325 space = psutil.disk_usage(directory)
328 min_free_disk *= 1024 * 1024
330 percent_free = 100.0 - space.percent
331 if percent_free < min_free_percent
and space.free < min_free_disk:
332 LOG.info(
'Low diskspace detected, cleaning cache')
334 bytes_needed = (min_free_percent - percent_free) / 100.0 * space.total
335 bytes_needed = int(bytes_needed + 1.0)
338 entries = (os.path.join(directory, fn)
for fn
in os.listdir(directory))
339 entries = ((os.stat(path), path)
for path
in entries)
342 entries = ((stat[ST_MTIME], stat[ST_SIZE], path)
343 for stat, path
in entries
if S_ISREG(stat[ST_MODE]))
347 for moddate, fsize, path
in sorted(entries):
354 if space_freed > bytes_needed:
359 """Get a directory for caching data 361 This directory can be used to hold temporary caches of data to 362 speed up performance. This directory will likely be part of a 363 small RAM disk and may be cleared at any time. So code that 364 uses these cached files must be able to fallback and regenerate 368 domain (str): The cache domain. Basically just a subdirectory. 371 str: a path to the directory where you can cache data 373 config = mycroft.configuration.Configuration.get()
374 dir = config.get(
"cache_path")
377 dir = os.path.join(tempfile.gettempdir(),
"mycroft",
"cache")
383 raise ValueError(
"Missing or empty %s in mycroft.conf " % name)
387 """Determine if Text to Speech is occurring 390 bool: True while still speaking 392 LOG.info(
"mycroft.utils.is_speaking() is depreciated, use " 393 "mycroft.audio.is_speaking() instead.")
398 """Pause as long as Text to Speech is still happening 400 Pause while Text to Speech is still happening. This always pauses 401 briefly to ensure that any preceeding request to speak has time to 404 LOG.info(
"mycroft.utils.wait_while_speaking() is depreciated, use " 405 "mycroft.audio.wait_while_speaking() instead.")
412 LOG.info(
"mycroft.utils.stop_speaking() is depreciated, use " 413 "mycroft.audio.stop_speaking() instead.")
418 """ Get architecture string of system. """ 424 Reset the sigint handler to the default. This fixes KeyboardInterrupt 425 not getting raised when started via start-mycroft.sh 427 sig.signal(sig.SIGINT, sig.default_int_handler)
431 """Helper to quickly create and start a thread with daemon = True""" 432 t = Thread(target=target, args=args, kwargs=kwargs)
439 """Blocks until KeyboardInterrupt is received""" 443 except KeyboardInterrupt:
447 _log_all_bus_messages =
False 451 """ Standard logging mechanism for Mycroft processes. 453 This handles the setup of the basic logging for all Mycroft 454 messagebus-based processes. 457 name (str): Reference name of the process 458 whitelist (list, optional): List of "type" strings. If defined, only 459 messages in this list will be logged. 462 func: The echo function 466 blacklist = Configuration.get().
get(
"ignore_logs")
470 whitelist.append(
'mycroft.debug.log')
473 global _log_all_bus_messages
475 msg = json.loads(message)
476 msg_type = msg.get(
"type",
"")
480 if whitelist
and not any([msg_type.startswith(e)
481 for e
in whitelist]):
484 if blacklist
and msg_type
in blacklist:
487 if msg_type ==
"mycroft.debug.log":
489 lvl = msg[
"data"].
get(
"level",
"").upper()
490 if lvl
in [
"CRITICAL",
"ERROR",
"WARNING",
"INFO",
"DEBUG"]:
492 LOG(name).info(
"Changing log level to: {}".format(lvl))
494 logging.getLogger().setLevel(lvl)
495 logging.getLogger(
'urllib3').setLevel(lvl)
499 LOG(name).info(
"Invalid level provided: {}".format(lvl))
502 log_bus = msg[
"data"].
get(
"bus",
None)
503 if log_bus
is not None:
504 LOG(name).info(
"Bus logging: {}".format(log_bus))
505 _log_all_bus_messages = log_bus
506 elif msg_type ==
"registration":
508 msg[
"data"][
"token"] =
None 509 message = json.dumps(msg)
510 except Exception
as e:
511 LOG.info(
"Error: {}".format(repr(e)), exc_info=
True)
513 if _log_all_bus_messages:
515 LOG(name).info(
"BUS: {}".format(message))
520 """Split camel case string""" 521 regex =
'.+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)' 522 matches = re.finditer(regex, identifier)
523 return ' '.join([m.group(0)
for m
in matches])
def create_daemon(target, args=(), kwargs=None)
def resolve_resource_file(res_name)
def find_input_device(device_name)
def read_dict(filename, div='=')
def wait_for_exit_signal()
def wait_while_speaking()
def wait_while_speaking()
def remove_last_slash(url)
def read_stripped_lines(filename)
def validate_param(value, name)
def create_echo_function(name, whitelist=None)
def curate_cache(directory, min_free_percent=5.0, min_free_disk=50)
def record(file_path, duration, rate, channels)
def reset_sigint_handler()
def ensure_directory_exists(directory, domain=None)
def get_cache_directory(domain=None)
def get(phrase, lang=None, context=None)
def connected_dns(host="8.8.8.8", port=53, timeout=3)