def systemctl(ctx: click.Context, option: str): click.secho(f'Service "{option}", please wait', fg='yellow') commons.waitAnimation() commons.sshCmd(f'sudo systemctl {option} ProjectAlice') commons.printSuccess('Done!') commons.returnToMainMenu(ctx, pause=True)
def upgradeSystem(ctx: click.Context): click.secho('Upgrading device\'s system, please wait', fg='yellow') commons.waitAnimation() commons.sshCmd('sudo apt-get update && sudo apt-get dist-upgrade -y') commons.printSuccess('Device upgraded!') ctx.invoke(reboot)
def updateSystem(ctx: click.Context): click.secho('Updating device\'s system, please wait', fg='yellow') commons.waitAnimation() commons.sshCmd('sudo apt-get update && sudo apt-get upgrade -y') commons.printSuccess('Device updated!') commons.returnToMainMenu(ctx, pause=True)
def reportBug(ctx: click.Context): click.secho('Enabling inbuilt bug reporter', fg='yellow') commons.waitAnimation() commons.sshCmd('touch ~/ProjectAlice/alice.bugreport') click.secho('Restarting Alice', fg='yellow') commons.sshCmd('sudo systemctl restart ProjectAlice') commons.printSuccess( 'Bug reporter enabled and Alice restart. As soon as a fatal error occurs and/or she is stopped, the session report will be posted to Github!' ) commons.returnToMainMenu(ctx, pause=True)
def displayLogs(ctx: click.Context, file: str): try: commons.sshCmd(f'tail -n 250 -f {file} & {{ read ; kill %1; }}') except: try: commons.SSH.exec_command('\r') except: address = commons.CONNECTED_TO ctx.invoke(commons.disconnect) if commons.tryReconnect(ctx, address): ctx.invoke(displayLogs, file) else: commons.printError('Connection to Alice lost') commons.returnToMainMenu(ctx)
def reboot(ctx: click.Context, return_to_main_menu: bool = True): # NOSONAR click.secho('Rebooting device, please wait', fg='yellow') commons.waitAnimation() commons.sshCmd('sudo reboot') address = commons.CONNECTED_TO ctx.invoke(commons.disconnect) rebooted = commons.tryReconnect(ctx, address) if not rebooted: commons.printError('Failed rebooting device') else: commons.printSuccess('Device rebooted!') if return_to_main_menu: commons.returnToMainMenu(ctx, pause=True)
def uninstallSoundDevice(ctx: click.Context, device: str, return_to_main_menu: bool = True): # NOSONAR click.secho('Uninstalling audio hardware', fg='yellow') commons.waitAnimation() if not device: device = getDeviceName() if not device: return if device.lower() in {'respeaker2', 'respeaker4', 'respeaker4miclineararray', 'respeaker6micarray'}: result = sshCmdWithReturn('test -d ~/seeed-voicecard/ && echo "1"')[0].readline() if result: sshCmd('cd ~/seeed-voicecard/ && sudo ./uninstall.sh') sshCmd('sudo rm -rf ~/seeed-voicecard/') ctx.invoke(reboot, return_to_main_menu=return_to_main_menu) commons.printSuccess('Sound device uninstalled!') if return_to_main_menu: commons.returnToMainMenu(ctx, pause=True)
def changeHostname(ctx: click.Context, hostname: str): click.secho('Changing device\'s hostname', fg='yellow') if not hostname: hostname = inquirer.text(message='Enter new device name', default='ProjectAlice', validate=lambda name: commons. validateHostname(name) is not None).execute() commons.waitAnimation() commons.sshCmd(f"sudo hostnamectl set-hostname '{hostname}'") ctx.invoke(reboot, ctx, return_to_main_menu=False) commons.waitAnimation() stdout, stderr = commons.sshCmdWithReturn('hostname') if stdout.readline().lower().strip() == hostname.lower().strip(): commons.printSuccess('Device name changed!') else: commons.printError('Failed changing device name...') commons.returnToMainMenu(ctx, pause=True)
def disableRespeakerLeds(ctx: click.Context): click.echo('Turning off Respeaker always on leds') commons.waitAnimation() click.echo('Make sure we have pip') commons.sshCmd('sudo apt-get install python3-pip -y') click.echo('Install pixel ring our savior') commons.sshCmd( 'sudo pip3 install pixel_ring' ) # sudo is required here, that's bad, but we uninstall directly after click.echo('Testing the leds and turning off') commons.sshCmd('pixel_ring_check', hide=True) time.sleep(3) click.echo('Uninstall pixel ring as we don\'t need it anymore') commons.sshCmd('sudo pip3 uninstall pixel_ring -y') commons.stopAnimation() commons.printSuccess('Should be done!') commons.returnToMainMenu(ctx, pause=True)
def installSoundDevice(ctx: click.Context, device: str): click.secho('Installing audio hardware', fg='yellow') commons.waitAnimation() if not device: device = getDeviceName() if not device: return ctx.invoke(uninstallSoundDevice, device=device, return_to_main_menu=False) commons.waitAnimation() sshCmd('sudo apt-get install git -y') if device.lower() in {'respeaker2', 'respeaker4', 'respeaker4miclineararray', 'respeaker6micarray'}: sshCmd('git clone https://github.com/HinTak/seeed-voicecard.git ~/seeed-voicecard/') sshCmd('git -C ~/seeed-voicecard/ checkout v5.9 && git -C ~/seeed-voicecard/ pull') sshCmd('cd ~/seeed-voicecard/ && sudo ./install.sh') ctx.invoke(reboot, return_to_main_menu=False) commons.printSuccess('Sound device installed!') commons.returnToMainMenu(ctx, pause=True)
def updateAlice(ctx: click.Context): click.secho('Updating Alice, please wait', fg='yellow') commons.waitAnimation() commons.sshCmd('sudo systemctl stop ProjectAlice') time.sleep(2) commons.sshCmd('rm ~/ProjectAlice/requirements.hash') commons.sshCmd('rm ~/ProjectAlice/sysrequirements.hash') commons.sshCmd('rm ~/ProjectAlice/pipuninstalls.hash') commons.sshCmd( 'cd ~/ProjectAlice && git pull && git submodules foreach git pull') time.sleep(2) commons.sshCmd('sudo systemctl start ProjectAlice') commons.printSuccess('Alice updated!') commons.returnToMainMenu(ctx, pause=True)
def soundTest(ctx: click.Context): commons.printInfo( 'This will test both the recording and playback of your device') click.pause( 'Press enter and read aloud: I am testing my sound input and output') commons.printInfo('Now recording...') commons.sshCmd('arecord /tmp/test.wav -d 3') time.sleep(3.5) commons.printInfo('Now playing...') commons.sshCmd('aplay /tmp/test.wav') time.sleep(3.5) confirm = inquirer.confirm( message='Did you hear yourself speaking through the device?', default=True).execute() if confirm: commons.printSuccess( 'Great, so both your mic and speakers are working!') else: commons.printInfo( "Ok... Sorry about that... Let's try the audio output only") click.pause() commons.printInfo('Testing sound output...') commons.waitAnimation() commons.sshCmd('sudo aplay /usr/share/sounds/alsa/Front_Center.wav') commons.stopAnimation() confirm = inquirer.confirm( message= 'Ok, I played it and you should have heard the common "front, center" sound test, did you hear it?', default=True).execute() if confirm: commons.printInfo( "Ok, so this means your audio output is fine, but your mic doesn't capture anything" ) click.pause() commons.sshCmd('arecord -l') confirm = inquirer.confirm( message='Do you see your mic listed in the list above?', default=True).execute() if confirm: commons.printInfo( 'Mmmh, here some potential fixes:\n- Use "alsamixer" to raise the capture volume of your device.\n- Edit your "/etc/asound.conf" to set it up correctly.\n- Try reaching out on our Discord server, maybe others had the same issue?' ) else: commons.printInfo( 'Mmmh, here some potential fixes:\n Edit your "/etc/asound.conf" to set it up correctly.\n- Try reaching out on our Discord server, maybe others had the same issue?' ) else: commons.printInfo( "Ok, so this would mean the output doesn't work, and maybe the input works, but you can't hear the result..." ) click.pause() commons.sshCmd('aplay -l') confirm = inquirer.confirm( message= 'Do you see your audio output device listed in the list above?', default=True).execute() if confirm: commons.printInfo( 'Mmmh, here some potential fixes:\n- Use "alsamixer" to raise the output volume of your device.\n- Edit your "/etc/asound.conf" to set it up correctly.\n- Try reaching out on our Discord server, maybe others had the same issue?' ) else: commons.printInfo( 'Mmmh, here some potential fixes:\n Edit your "/etc/asound.conf" to set it up correctly.\n- Try reaching out on our Discord server, maybe others had the same issue?' ) commons.returnToMainMenu(ctx, pause=True)
def installAlice(ctx: click.Context, force: bool): click.secho('\nInstalling Alice!', fg='yellow') result = sshCmdWithReturn('test -d ~/ProjectAlice/ && echo "1"')[0].readline() if result: if not force: commons.printError('Alice seems to already exist on that host') confirm = inquirer.confirm( message='Erase and reinstall?', default=False ).execute() if confirm: commons.returnToMainMenu(ctx) return sshCmd('sudo systemctl stop ProjectAlice') sshCmd('sudo rm -rf ~/ProjectAlice') adminPinCode = inquirer.secret( message='Enter an admin pin code. It must be made of 4 characters, all digits only. (default: 1234)', default='1234', validate=lambda code: code.isdigit() and int(code) < 10000, invalid_message='Pin must be 4 numbers', transformer=lambda _: commons.HIDDEN ).execute() mqttHost = inquirer.text( message='Mqtt hostname', default='localhost', validate=EmptyInputValidator(commons.NO_EMPTY) ).execute() mqttPort = inquirer.number( message='Mqtt port', default=1883, validate=EmptyInputValidator(commons.NO_EMPTY) ).execute() activeLanguage = inquirer.select( message='What language should Alice be using?', default='en', choices=[ Choice('en', name='English'), Choice('de', name='German'), Choice('fr', name='French'), Choice('it', name='Italian'), Choice('pl', name='Polish'), ] ).execute() activeCountryCode = inquirer.select( message='Enter the country code to use.', default='EN', choices=commons.COUNTRY_CODES ).execute() releaseType = inquirer.select( message='What releases do you want to receive? If you are unsure, leave this to Stable releases!', default='master', choices=[ Choice('master', name='Stable releases'), Choice('rc', name='Release candidates'), Choice('beta', name='Beta releases'), Choice('alpha', name='Alpha releases') ] ).execute() audioDevice = inquirer.select( message='Select your audio hardware if listed', default='respeaker2', choices=[ Choice('respeaker2', name='Respeaker 2 mics'), Choice('respeaker4', name='Respeaker 4 mic array'), Choice('respeaker4MicLinear', name='Respeaker 4 mic linear array'), Choice('respeaker6', name='Respeaker 6 mic array'), Choice('respeaker7', name='Respeaker 7'), Choice('respeakerCoreV2', name='Respeaker Core version 2'), Choice('googleAIY', name='Google AIY'), Choice('googleAIY2', name='Google AIY 2'), Choice('matrixCreator', name='Matrix Creator'), Choice('matrixVoice', name='Matrix Voice'), Choice('ps3eye', name='PS3 Eye'), Choice('jabra410', name='Jabra 410'), Choice(None) ] ).execute() soundInstalled = inquirer.confirm( message='Did you already install your sound hardware using Alice CLI or confirmed it to be working?', default=False ).execute() installHLC = inquirer.confirm( message='Do you want to install HLC? HLC can pilot leds such as the ones on Respeakers to provide visual feedback.', default=False ).execute() advancedConfigs = inquirer.confirm( message='Do you want to set more advanced configs? If you do, DO NOT ABORT IN THE MIDDLE!', default=False ).execute() asr = 'coqui' tts = 'pico' awsAccessKey = '' awsSecretKey = '' googleServiceFile = '' shortReplies = False devMode = False githubUsername = '' githubToken = '' enableDataStoring = True skillAutoUpdate = True if advancedConfigs: asr = inquirer.select( message='Select the ASR engine you want to use', default='coqui', choices=[ Choice('coqui', name='Coqui'), Choice('snips', name='Snips (English only!)'), Choice('google', name='Google (Online!)'), Choice('deepspeech', name='Deepspeech'), Choice('pocketsphinx', name='PocketSphinx') ] ).execute() tts = inquirer.select( message='Select the TTS engine you want to use', default='pico', choices=[ Choice('pico', name='Coqui'), Choice('mycroft', name='Mycroft'), Choice('amazon', name='Amazon'), Choice('google', name='Google'), Choice('watson', name='Watson') ] ).execute() if tts == 'amazon': awsAccessKey = inquirer.secret( message='Enter your AWS access key', validate=EmptyInputValidator(commons.NO_EMPTY), transformer=lambda _: commons.HIDDEN ).execute() awsSecretKey = inquirer.secret( message='Enter your AWS secret key', validate=EmptyInputValidator(commons.NO_EMPTY), transformer=lambda _: commons.HIDDEN ).execute() if tts == 'google' or asr == 'google': googleServiceFile = inquirer.filepath( message='Enter your Google service file path', default='~/', validate=PathValidator(is_file=True, message='Input is not a file') ).execute() shortReplies = inquirer.confirm( message='Do you want Alice to use short replies?', default=False ).execute() devMode = inquirer.confirm( message='Do you want to activate the developer mode?', default=False ).execute() if devMode: githubUsername = inquirer.text( message='Enter your Github username. This is required for Dev Mode.', validate=EmptyInputValidator(commons.NO_EMPTY) ).execute() githubToken = inquirer.secret( message='Enter your Github access token. This is required for Dev Mode.', validate=EmptyInputValidator(commons.NO_EMPTY), transformer=lambda _: commons.HIDDEN ).execute() enableDataStoring = inquirer.confirm( message='Enable telemetry data storing?', default=True ).execute() skillAutoUpdate = inquirer.confirm( message='Enable skill auto update?', default=True ).execute() commons.waitAnimation() confFile = Path(Path.home(), '.pacli/projectalice.yaml') confFile.parent.mkdir(parents=True, exist_ok=True) updateSource = commons.getUpdateSource(releaseType) try: with requests.get(url=f'https://raw.githubusercontent.com/project-alice-assistant/ProjectAlice/{updateSource}/ProjectAlice.yaml', stream=True) as r: r.raise_for_status() with confFile.open('wb') as fp: for chunk in r.iter_content(chunk_size=8192): if chunk: fp.write(chunk) except Exception as e: commons.printError(f'Failed downloading ProjectAlice.yaml {e}') commons.returnToMainMenu(ctx, pause=True) with confFile.open(mode='r') as f: try: confs = yaml.safe_load(f) except yaml.YAMLError as e: commons.printError(f'Failed reading projectalice.yaml {e}') commons.returnToMainMenu(ctx, pause=True) confs['adminPinCode'] = str(int(adminPinCode)).zfill(4) confs['mqttHost'] = mqttHost confs['mqttPort'] = int(mqttPort) confs['activeLanguage'] = activeLanguage confs['activeCountryCode'] = activeCountryCode confs['useHLC'] = installHLC confs['aliceUpdateChannel'] = releaseType confs['skillsUpdateChannel'] = releaseType confs['ttsLanguage'] = f'{activeLanguage}-{activeCountryCode}' if soundInstalled: confs['installSound'] = False if audioDevice: confs['audioHardware'][audioDevice] = True else: confs['installSound'] = True confs['asr'] = asr confs['awsAccessKey'] = awsAccessKey confs['awsSecretKey'] = awsSecretKey confs['tts'] = tts confs['shortReplies'] = shortReplies confs['devMode'] = devMode confs['githubUsername'] = githubUsername confs['githubToken'] = githubToken confs['enableDataStoring'] = enableDataStoring confs['skillAutoUpdate'] = skillAutoUpdate if googleServiceFile and Path(googleServiceFile).exists(): confs['googleServiceFile'] = Path(googleServiceFile).read_text() if asr == 'google': confs['keepASROffline'] = False if tts in ['amazon', 'google', 'watson']: confs['keepTTSOffline'] = False with confFile.open(mode='w') as f: yaml.dump(confs, f) commons.printSuccess('Generated ProjectAlice.yaml') commons.printInfo('Updating system') sshCmd('sudo apt-get update') sshCmd('sudo apt-get install git -y') sshCmd('git config --global user.name "Han Oter"') sshCmd('git config --global user.email "*****@*****.**"') commons.printInfo('Cloning Alice') sshCmd('git clone https://github.com/project-alice-assistant/ProjectAlice.git ~/ProjectAlice') sftp = commons.SSH.open_sftp() sftp.put(str(confFile), '/home/pi/ProjectAlice/ProjectAlice.yaml') sftp.close() sshCmd('sudo rm /boot/ProjectAlice.yaml') sshCmd('sudo cp ~/ProjectAlice/ProjectAlice.yaml /boot/ProjectAlice.yaml') commons.printInfo('Start install process') sshCmd('cd ~/ProjectAlice/ && python3 main.py') commons.printSuccess('Alice has completed the basic installation! She\'s now working further to complete the installation, let\'s see what she does!') ctx.invoke(systemLogs)