def test_keyboard_interrupt(self): l1 = Lock('test') self.assertTrue(isfile(get_temp_path('mycroft', 'test.pid'))) try: os.kill(os.getpid(), signal.SIGINT) except KeyboardInterrupt: pass self.assertFalse(isfile(get_temp_path('mycroft', 'test.pid')))
def test_multiple_existing(self, mock_glob): mock_glob.return_value = [ get_temp_path('test.part'), get_temp_path('test.part.1'), get_temp_path('test.part.2') ] dest = get_temp_path('test') self.assertEqual(_get_download_tmp(dest), dest + '.part.3')
def __init__(self, lang, config, validator, audio_ext='wav', phonetic_spelling=True, ssml_tags=None): super(TTS, self).__init__() self.bus = None # initalized in "init" step self.lang = lang or 'en-us' self.config = config self.validator = validator self.phonetic_spelling = phonetic_spelling self.audio_ext = audio_ext self.ssml_tags = ssml_tags or [] self.voice = config.get("voice") self.filename = get_temp_path('tts.wav') self.enclosure = None random.seed() self.queue = Queue() self.playback = PlaybackThread(self.queue) self.playback.start() self.spellings = self.load_spellings() self.tts_name = type(self).__name__ self.cache = TextToSpeechCache(self.config, self.tts_name, self.audio_ext) self.cache.clear()
def test_existing_lock(self, mock_kill): """ Test that an existing lock will kill the old pid. """ l1 = Lock('test') self.assertTrue(isfile(get_temp_path('mycroft', 'test.pid'))) l2 = Lock('test2') self.assertFalse(mock_kill.called) l2 = Lock('test') self.assertTrue(mock_kill.called)
def _init_msm_lock(): msm_lock = None try: msm_lock = ComboLock(get_temp_path('mycroft-msm.lck')) LOG.debug('mycroft-msm combo lock instantiated') except Exception: LOG.exception('Failed to create msm lock!') return msm_lock
def test_record_without_duration(self, mock_subprocess): mock_proc = mock.Mock(name='mock process') mock_subprocess.Popen.return_value = mock_proc rate = 16000 channels = 1 filename = get_temp_path('test.wav') duration = 0 res = record(filename, duration, rate, channels) mock_subprocess.Popen.assert_called_once_with( ['arecord', '-r', str(rate), '-c', str(channels), filename]) self.assertEqual(res, mock_proc)
def __init__(self, bus=None): self.msm_lock = ComboLock(get_temp_path('mycroft-msm.lck')) self.install_retries = 0 self.config = Configuration.get() update_interval = self.config['skills']['update_interval'] self.update_interval = int(update_interval) * ONE_HOUR self.dot_msm_path = os.path.join(self.msm.skills_dir, '.msm') self.next_download = self._determine_next_download_time() self._log_next_download_time() self.installed_skills = set() self.default_skill_install_error = False if bus: self._register_bus_handlers()
class RemoteConf(LocalConf): _lock = ComboLock(get_temp_path('remote-conf.lock')) """Config dictionary fetched from mycroft.ai.""" def __init__(self, cache=None): super(RemoteConf, self).__init__(None) cache = cache or join(xdg.BaseDirectory.xdg_cache_home, 'mycroft', 'web_cache.json') from mycroft.api import is_paired if not is_paired(): self.load_local(cache) return try: # Here to avoid cyclic import from mycroft.api import DeviceApi api = DeviceApi() setting = api.get_settings() location = None try: location = api.get_location() except RequestException as e: LOG.error( "RequestException fetching remote location: {}".format( str(e))) if exists(cache) and isfile(cache): location = load_commented_json(cache).get('location') if location: setting["location"] = location # Remove server specific entries config = {} translate_remote(config, setting) for key in config: self.__setitem__(key, config[key]) self.store(cache) except RequestException as e: LOG.error( "RequestException fetching remote configuration: {}".format( str(e))) self.load_local(cache) except Exception as e: LOG.error("Failed to fetch remote configuration: %s" % repr(e), exc_info=True) self.load_local(cache)
class LocalConf(dict): """Config dictionary from file.""" _lock = ComboLock(get_temp_path('local-conf.lock')) def __init__(self, path): super(LocalConf, self).__init__() if path: self.path = path self.load_local(path) def load_local(self, path): """Load local json file into self. Args: path (str): file to load """ if exists(path) and isfile(path): try: config = load_commented_json(path) for key in config: self.__setitem__(key, config[key]) LOG.debug("Configuration {} loaded".format(path)) except Exception as e: LOG.error("Error loading configuration '{}'".format(path)) LOG.error(repr(e)) else: LOG.debug("Configuration '{}' not defined, skipping".format(path)) def store(self, path=None): """Cache the received settings locally. The cache will be used if the remote is unreachable to load settings that are as close to the user's as possible. """ with self._lock: path = path or self.path config_dir = dirname(path) if not exists(config_dir): os.makedirs(config_dir) with open(path, 'w') as f: json.dump(self, f, indent=2) def merge(self, conf): merge_dict(self, conf)
def test_no_existing(self, mock_glob): mock_glob.return_value = [] dest = get_temp_path('test') self.assertEqual(_get_download_tmp(dest), dest + '.part')
from threading import Event from unittest import TestCase, mock from mycroft.util.download import (download, _running_downloads, _get_download_tmp) from mycroft.util.file_utils import get_temp_path TEST_URL = 'http://example.com/mycroft-test.tar.gz' TEST_DEST = get_temp_path('file.tar.gz') @mock.patch('mycroft.util.download.subprocess') @mock.patch('mycroft.util.download.os') class TestDownload(TestCase): def setUp(self): """Remove any cached instance.""" for key in list(_running_downloads.keys()): _running_downloads.pop(key) def test_download_basic(self, mock_os, mock_subprocess): """Test the basic download call.""" mock_subprocess.call.return_value = 0 downloader = download(url=TEST_URL, dest=TEST_DEST) downloader.join() mock_subprocess.call.assert_called_once_with([ 'wget', '-c', TEST_URL, '-O', TEST_DEST + '.part', '--tries=20', '--read-timeout=5' ]) self.assertTrue(downloader.done)
def process(self, data): # TODO: Look into removing this emit altogether. # We need to check if any other serial bus messages # are handled by other parts of the code if "mycroft.stop" not in data: self.bus.emit(Message(data)) if "Command: system.version" in data: # This happens in response to the "system.version" message # sent during the construction of Enclosure() self.bus.emit(Message("enclosure.started")) if "mycroft.stop" in data: if has_been_paired(): create_signal('buttonPress') self.bus.emit(Message("mycroft.stop")) if "volume.up" in data: self.bus.emit( Message("mycroft.volume.increase", {'play_sound': True})) if "volume.down" in data: self.bus.emit( Message("mycroft.volume.decrease", {'play_sound': True})) if "system.test.begin" in data: self.bus.emit(Message('recognizer_loop:sleep')) if "system.test.end" in data: self.bus.emit(Message('recognizer_loop:wake_up')) if "mic.test" in data: mixer = Mixer() prev_vol = mixer.getvolume()[0] mixer.setvolume(35) self.bus.emit( Message("speak", {'utterance': "I am testing one two three"})) time.sleep(0.5) # Prevents recording the loud button press record(get_temp_path('test.wav', 3.0)) mixer.setvolume(prev_vol) play_wav(get_temp_path('test.wav')).communicate() # Test audio muting on arduino subprocess.call('speaker-test -P 10 -l 0 -s 1', shell=True) if "unit.shutdown" in data: # Eyes to soft gray on shutdown self.bus.emit( Message("enclosure.eyes.color", { 'r': 70, 'g': 65, 'b': 69 })) self.bus.emit( Message("enclosure.eyes.timedspin", {'length': 12000})) self.bus.emit(Message("enclosure.mouth.reset")) time.sleep(0.5) # give the system time to pass the message self.bus.emit(Message("system.shutdown")) if "unit.reboot" in data: # Eyes to soft gray on reboot self.bus.emit( Message("enclosure.eyes.color", { 'r': 70, 'g': 65, 'b': 69 })) self.bus.emit(Message("enclosure.eyes.spin")) self.bus.emit(Message("enclosure.mouth.reset")) time.sleep(0.5) # give the system time to pass the message self.bus.emit(Message("system.reboot")) if "unit.setwifi" in data: self.bus.emit(Message("system.wifi.setup", {'lang': self.lang})) if "unit.factory-reset" in data: self.bus.emit( Message("speak", { 'utterance': mycroft.dialog.get("reset to factory defaults") })) subprocess.call('rm ~/.mycroft/identity/identity2.json', shell=True) self.bus.emit(Message("system.wifi.reset")) self.bus.emit(Message("system.ssh.disable")) wait_while_speaking() self.bus.emit(Message("enclosure.mouth.reset")) self.bus.emit(Message("enclosure.eyes.spin")) self.bus.emit(Message("enclosure.mouth.reset")) time.sleep(5) # give the system time to process all messages self.bus.emit(Message("system.reboot")) if "unit.enable-ssh" in data: # This is handled by the wifi client self.bus.emit(Message("system.ssh.enable")) self.bus.emit( Message("speak", {'utterance': mycroft.dialog.get("ssh enabled")})) if "unit.disable-ssh" in data: # This is handled by the wifi client self.bus.emit(Message("system.ssh.disable")) self.bus.emit( Message("speak", {'utterance': mycroft.dialog.get("ssh disabled")})) if "unit.enable-learning" in data or "unit.disable-learning" in data: enable = 'enable' in data word = 'enabled' if enable else 'disabled' LOG.info("Setting opt_in to: " + word) new_config = {'opt_in': enable} user_config = LocalConf(USER_CONFIG) user_config.merge(new_config) user_config.store() self.bus.emit( Message("speak", {'utterance': mycroft.dialog.get("learning " + word)}))
def test_delete_lock(self): l1 = Lock('test') self.assertTrue(isfile(get_temp_path('mycroft', 'test.pid'))) l1.delete() self.assertFalse(isfile(get_temp_path('mycroft', 'test.pid')))
def test_create_lock(self): l1 = Lock('test') self.assertTrue(isfile(get_temp_path('mycroft', 'test.pid')))
def setUp(self): if exists(get_temp_path('mycroft')): rmtree(get_temp_path('mycroft'))
def main(): parser = argparse.ArgumentParser() parser.add_argument('-f', '--filename', dest='filename', default=get_temp_path('test.wav'), help="Filename for saved audio (Default:{}".format( get_temp_path('test.wav'))) parser.add_argument('-d', '--duration', dest='duration', type=int, default=10, help="Duration of recording in seconds (Default: 10)") parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', default=False, help="Add extra output regarding the recording") parser.add_argument('-l', '--list', dest='show_devices', action='store_true', default=False, help="List all availabile input devices") args = parser.parse_args() if args.show_devices: print(" Initializing... ") pa = pyaudio.PyAudio() print(" ====================== Audio Devices ======================") print(" Index Device Name") for device_index in range(pa.get_device_count()): dev = pa.get_device_info_by_index(device_index) if dev['maxInputChannels'] > 0: print(' {}: {}'.format(device_index, dev['name'])) print() config = Configuration.get() if "device_name" in config["listener"]: dev = config["listener"]["device_name"] elif "device_index" in config["listener"]: dev = "Device at index {}".format(config["listener"]["device_index"]) else: dev = "Default device" samplerate = config["listener"]["sample_rate"] play_cmd = config["play_wav_cmdline"].replace("%1", "WAV_FILE") print(" ========================== Info ===========================") print(" Input device: {} @ Sample rate: {} Hz".format(dev, samplerate)) print(" Playback commandline: {}".format(play_cmd)) print() print(" ===========================================================") print(" == STARTING TO RECORD, MAKE SOME NOISE! ==") print(" ===========================================================") if not args.verbose: with mute_output(): record(args.filename, args.duration) else: record(args.filename, args.duration) print(" ===========================================================") print(" == DONE RECORDING, PLAYING BACK... ==") print(" ===========================================================") status = play_wav(args.filename).wait() if status: print('An error occured while playing back audio ({})'.format(status))
class Lock: # python 3+ 'class Lock' """ Create and maintains the PID lock file for this application process. The PID lock file is located in /tmp/mycroft/*.pid. If another process of the same type is started, this class will 'attempt' to stop the previously running process and then change the process ID in the lock file. """ # # Class constants DIRECTORY = get_temp_path('mycroft') FILE = '/{}.pid' # # Constructor def __init__(self, service): """ Builds the instance of this object. Holds the lock until the object is garbage collected. service: Text string. The name of the service application to be locked (ie: skills, voice) """ super(Lock, self).__init__() # python 3+ 'super().__init__()' self.__pid = os.getpid() # PID of this application self.path = Lock.DIRECTORY + Lock.FILE.format(service) self.set_handlers() # set signal handlers self.create() # # Reset the signal handlers to the 'delete' function def set_handlers(self): """ Trap both SIGINT and SIGTERM to gracefully clean up PID files """ self.__handlers = { SIGINT: Signal(SIGINT, self.delete), SIGTERM: Signal(SIGTERM, self.delete) } # # Check to see if the PID already exists # If it does exits perform several things: # Stop the current process # Delete the exiting file def exists(self): """ Check if the PID lock file exists. If it does then send a SIGKILL signal to the process defined by the value in the lock file. Catch the keyboard interrupt exception to prevent propagation if stopped by use of Ctrl-C. """ if not os.path.isfile(self.path): return with open(self.path, 'r') as L: try: os.kill(int(L.read()), SIGKILL) except Exception as E: pass # # Create a lock file for this server process def touch(self): """ If needed, create the '/tmp/mycroft' directory than open the lock file for writting and store the current process ID (PID) as text. """ if not os.path.exists(Lock.DIRECTORY): os.makedirs(Lock.DIRECTORY) with open(self.path, 'w') as L: L.write('{}'.format(self.__pid)) # # Create the PID file def create(self): """ Checks to see if a lock file for this service already exists, if so have it killed. In either case write the process ID of the current service process to to the existing or newly created lock file in /tmp/mycroft/ """ self.exists() # check for current running process self.touch() # # Delete the PID file - but only if it has not been overwritten # by a duplicate service application def delete(self, *args): """ If the PID lock file contains the PID of this process delete it. *args: Ignored. Required as this fuction is called as a signel handler. """ try: with open(self.path, 'r') as L: pid = int(L.read()) if self.__pid == pid: os.unlink(self.path) except IOError: pass