Skip to content

Commit 12e3f50

Browse files
Merge branch '1.0.0-rc2' into master
2 parents aadaa89 + aa2811a commit 12e3f50

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+2645
-2233
lines changed

.gitmodules

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
[submodule "core/webui/public"]
22
path = core/webui/public
33
url = https://github.com/project-alice-assistant/webui.git
4-
branch = master
4+
branch = master

configTemplate.json

+14-6
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,15 @@
1717
"dataType" : "boolean",
1818
"isSensitive" : false,
1919
"description" : "Displays the current system usage on the interface",
20-
"category" : "system"
20+
"category" : "system",
21+
"onUpdate" : "WebUIManager.toggleSystemUsage"
22+
},
23+
"enableSSL" : {
24+
"defaultValue": false,
25+
"dataType" : "boolean",
26+
"isSensitive" : false,
27+
"description" : "Enables SSL for both the UI and API",
28+
"category" : "system"
2129
},
2230
"delegateNluTraining" : {
2331
"defaultValue": false,
@@ -435,7 +443,7 @@
435443
"IBM Watson" : "watson"
436444
},
437445
"description" : "The Tts to use. Can't use an online Tts if you have set keepTTSOffline!",
438-
"onUpdate" : "reloadTTS",
446+
"onUpdate" : "reloadTTSManager",
439447
"category" : "tts"
440448
},
441449
"ttsFallback" : {
@@ -450,15 +458,15 @@
450458
"IBM Watson" : "watson"
451459
},
452460
"description" : "The Tts to use in case the default Tts becomes unavailable.",
453-
"onUpdate" : "reloadTTS",
461+
"onUpdate" : "reloadTTSManager",
454462
"category" : "tts"
455463
},
456464
"ttsLanguage" : {
457465
"defaultValue": "en-US",
458466
"dataType" : "string",
459467
"isSensitive" : false,
460468
"description" : "Language for the Tts to use",
461-
"onUpdate" : "reloadTTS",
469+
"onUpdate" : "reloadTTSManager",
462470
"category" : "tts"
463471
},
464472
"ttsType" : {
@@ -470,7 +478,7 @@
470478
"female"
471479
],
472480
"description" : "Choose the voice gender you want",
473-
"onUpdate" : "reloadTTS",
481+
"onUpdate" : "reloadTTSManager",
474482
"category" : "tts"
475483
},
476484
"ttsNeural" : {
@@ -485,7 +493,7 @@
485493
"dataType" : "string",
486494
"isSensitive" : false,
487495
"description" : "The voice the Tts should use",
488-
"onUpdate" : "reloadTTS",
496+
"onUpdate" : "reloadTTSManager",
489497
"category" : "tts",
490498
"display" : "hidden"
491499
},

core/Initializer.py

+61-47
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
ASOUND = '/etc/asound.conf'
3737
TEMP = Path('/tmp/service')
3838
ALLOWED_LANGUAGES = {'en', 'de', 'fr', 'it', 'pt', 'pl'}
39+
FALLBACK_ASR = 'coqui'
3940

4041

4142
class InitDict(dict):
@@ -46,40 +47,46 @@ def __init__(self, default: dict):
4647

4748
def __getitem__(self, item):
4849
try:
49-
item = super().__getitem__(item)
50-
if not item:
50+
value = super().__getitem__(item)
51+
if value is None:
5152
raise Exception
52-
return item
53+
return value
5354
except:
5455
print(f'Missing key **{item}** in provided yaml file.')
5556
return ''
5657

5758

58-
class SimpleLogger:
59+
class SimpleLogger(object):
5960

6061
def __init__(self, prepend: str = None):
61-
self._prepend = f'[{prepend}]\t '
62+
self._prepend = f'[{prepend}]'
6263
self._logger = logging.getLogger('ProjectAlice')
6364

6465

6566
def logInfo(self, text: str):
66-
self._logger.info(f'{self._prepend} {text}')
67+
self._logger.info(f'{self.spacer(text)}')
6768

6869

6970
def logWarning(self, text: str):
70-
self._logger.warning(f'{self._prepend} {text}')
71+
self._logger.warning(f'{self.spacer(text)}')
7172

7273

7374
def logError(self, text: str):
74-
self._logger.error(f'{self._prepend} {text}')
75+
self._logger.error(f'{self.spacer(text)}')
7576

7677

7778
def logFatal(self, text: str):
78-
self._logger.fatal(f'{self._prepend} {text}')
79+
self._logger.fatal(f'{self.spacer(text)}')
7980
exit(1)
8081

8182

82-
class PreInit:
83+
def spacer(self, msg: str) -> str:
84+
space = ''.join([' ' for _ in range(35 - len(self._prepend) + 1)])
85+
msg = f'{self._prepend}{space}{msg}'
86+
return msg
87+
88+
89+
class PreInit(object):
8390
"""
8491
Pre init checks and makes sure vital stuff is installed and running. Not much, but internet, venv and so on
8592
Pre init is meant to run on the system python and not on the venv
@@ -209,7 +216,7 @@ def doUpdates(self):
209216
updateSource = self.getUpdateSource(updateChannel)
210217
# Update our system and sources
211218
subprocess.run(['sudo', 'apt-get', 'update'])
212-
#subprocess.run(['sudo', 'apt-get', 'dist-upgrade', '-y'])
219+
subprocess.run(['sudo', 'apt-get', 'dist-upgrade', '-y'])
213220
subprocess.run(['sudo', 'apt', 'autoremove', '-y'])
214221
subprocess.run(['git', 'clean', '-df'])
215222
subprocess.run(['git', 'stash'])
@@ -316,7 +323,7 @@ def checkVenv(self) -> bool:
316323
if not Path('venv').exists():
317324
self._logger.logInfo('Not running with venv, I need to create it')
318325
subprocess.run(['sudo', 'apt-get', 'install', 'python3-dev', 'python3-pip', 'python3-venv', 'python3-wheel', '-y'])
319-
subprocess.run(['python3', '-m', 'venv', 'venv'])
326+
subprocess.run(['python3.7', '-m', 'venv', 'venv'])
320327
self.updateVenv()
321328
self._logger.logInfo('Installed virtual environement, restarting...')
322329
return False
@@ -331,7 +338,7 @@ def checkVenv(self) -> bool:
331338
def updateVenv(self):
332339
subprocess.run([self.PIP, 'uninstall', '-y', '-r', str(Path(self.rootDir, 'pipuninstalls.txt'))])
333340
subprocess.run([self.PIP, 'install', 'wheel'])
334-
subprocess.run([self.PIP, 'install', '-r', str(Path(self.rootDir, 'requirements.txt'))])
341+
subprocess.run([self.PIP, 'install', '-r', str(Path(self.rootDir, 'requirements.txt')), '--upgrade', '--no-cache-dir'])
335342

336343

337344
@staticmethod
@@ -355,7 +362,7 @@ def setServiceFileTo(pointer: str):
355362
time.sleep(1)
356363

357364

358-
class Initializer:
365+
class Initializer(object):
359366
PIP = './venv/bin/pip'
360367

361368

@@ -396,7 +403,7 @@ def initProjectAlice(self) -> bool: # NOSONAR
396403
self._logger.logFatal('Unfortunately it won\'t be possible, config sample is not existing')
397404
return False
398405

399-
self._confsFile.write_text(json.dumps(self._confsSample.read_text(), indent='\t'))
406+
self._confsFile.write_text(self._confsSample.read_text())
400407

401408
try:
402409
confs = json.loads(self._confsFile.read_text())
@@ -427,6 +434,10 @@ def initProjectAlice(self) -> bool: # NOSONAR
427434
subprocess.run(['sudo', 'systemctl', 'stop', 'mosquitto'])
428435
subprocess.run('sudo sed -i -e \'s/persistence true/persistence false/\' /etc/mosquitto/mosquitto.conf'.split())
429436
subprocess.run(['sudo', 'rm', '/var/lib/mosquitto/mosquitto.db'])
437+
subprocess.run(['sudo', 'systemctl', 'start', 'mosquitto'])
438+
439+
subprocess.run(['sudo', 'systemctl', 'stop', 'nginx'])
440+
subprocess.run(['sudo', 'systemctl', 'disable', 'nginx'])
430441

431442
# Now let's dump some values to their respective places
432443
# First those that need some checks and self filling in case unspecified
@@ -435,20 +446,20 @@ def initProjectAlice(self) -> bool: # NOSONAR
435446

436447
pinCode = initConfs['adminPinCode']
437448
try:
438-
pin = int(pinCode)
439-
if len(str(pin)) != 4:
449+
if len(str(pinCode)) != 4:
440450
raise Exception
451+
int(pinCode)
441452
except:
442453
self._logger.logFatal('Pin code must be 4 digits')
443454

444-
confs['adminPinCode'] = int(pinCode)
455+
confs['adminPinCode'] = pinCode
445456

446457
confs['stayCompletelyOffline'] = bool(initConfs['stayCompletelyOffline'] or False)
447458
if confs['stayCompletelyOffline']:
448459
confs['keepASROffline'] = True
449460
confs['keepTTSOffline'] = True
450461
confs['skillAutoUpdate'] = False
451-
confs['asr'] = 'deepspeech'
462+
confs['asr'] = FALLBACK_ASR
452463
confs['tts'] = 'pico'
453464
confs['awsRegion'] = ''
454465
confs['awsAccessKey'] = ''
@@ -462,14 +473,14 @@ def initProjectAlice(self) -> bool: # NOSONAR
462473
confs['awsAccessKey'] = initConfs['awsAccessKey']
463474
confs['awsSecretKey'] = initConfs['awsSecretKey']
464475

465-
confs['asr'] = initConfs['asr'] if initConfs['asr'] in {'pocketsphinx', 'google', 'deepspeech', 'snips', 'coqui'} else 'deepspeech'
476+
confs['asr'] = initConfs['asr'] if initConfs['asr'] in {'pocketsphinx', 'google', 'deepspeech', 'snips', 'coqui'} else FALLBACK_ASR
466477
if confs['asr'] == 'google' and not initConfs['googleServiceFile']:
467-
self._logger.logInfo('You cannot use Google Asr without a google service file, falling back to Deepspeech')
468-
confs['asr'] = 'deepspeech'
478+
self._logger.logInfo(f'You cannot use Google Asr without a google service file, falling back to {FALLBACK_ASR}')
479+
confs['asr'] = FALLBACK_ASR
469480

470481
if confs['asr'] == 'snips' and confs['activeLanguage'] != 'en':
471-
self._logger.logInfo('You can only use Snips Asr for english, falling back to Deepspeech')
472-
confs['asr'] = 'deepspeech'
482+
self._logger.logInfo(f'You can only use Snips Asr for english, falling back to {FALLBACK_ASR}')
483+
confs['asr'] = FALLBACK_ASR
473484

474485
if initConfs['googleServiceFile']:
475486
googleCreds = Path(self._rootDir, 'credentials/googlecredentials.json')
@@ -519,6 +530,7 @@ def initProjectAlice(self) -> bool: # NOSONAR
519530
try:
520531
import pkg_resources
521532

533+
self._logger.logInfo("*** Trying to load SNIPS-NLU.")
522534
pkg_resources.require('snips-nlu')
523535
subprocess.run(['./venv/bin/snips-nlu', 'download', confs['activeLanguage']])
524536
except:
@@ -557,48 +569,50 @@ def initProjectAlice(self) -> bool: # NOSONAR
557569
confs['disableSound'] = False
558570
confs['disableCapture'] = False
559571

572+
hlcDir = Path('/home', getpass.getuser(), 'HermesLedControl')
560573
hlcServiceFilePath = Path('/etc/systemd/system/hermesledcontrol.service')
561-
hlcConfigFilePath = Path(f'/home/{getpass.getuser()}/hermesLedControl/configuration.yml')
574+
hlcDistributedServiceFilePath = hlcDir / 'hermesledcontrol.service'
575+
hlcConfigTemplatePath = hlcDir / 'configuration.yml'
562576
hlcConfig = dict()
563577
if initConfs['useHLC']:
564-
565-
hlcDir = Path('/home', getpass.getuser(), 'hermesLedControl')
578+
self._logger.logInfo("*** Taking care of HLC.")
566579

567580
if not hlcDir.exists():
568-
subprocess.run(['git', 'clone', 'https://github.com/project-alice-assistant/hermesLedControl.git', str(Path('/home', getpass.getuser(), 'hermesLedControl'))])
581+
#todo: replace with AliceGit maybe?
582+
subprocess.run(['git', 'clone', 'https://github.com/project-alice-assistant/hermesLedControl.git', str(hlcDir)])
569583
else:
570584
subprocess.run(['git', '-C', hlcDir, 'stash'])
571585
subprocess.run(['git', '-C', hlcDir, 'pull'])
572586
subprocess.run(['git', '-C', hlcDir, 'stash', 'clear'])
573587

574588
if hlcServiceFilePath.exists():
575589
subprocess.run(['sudo', 'systemctl', 'stop', 'hermesledcontrol'])
590+
subprocess.run(['sudo', 'systemctl', 'disable', 'hermesledcontrol'])
576591
subprocess.run(['sudo', 'rm', hlcServiceFilePath])
577592

578-
subprocess.run(['python3', '-m', 'venv', f'/home/{getpass.getuser()}/hermesLedControl/venv'])
579-
580-
reqs = [
581-
'RPi.GPIO',
582-
'spidev',
583-
'gpiozero',
584-
'paho-mqtt',
585-
'numpy',
586-
'pyyaml'
587-
]
588-
for req in reqs:
589-
subprocess.run([f'/home/{getpass.getuser()}/hermesLedControl/venv/bin/pip', 'install', req])
593+
subprocess.run(['python3.7', '-m', 'venv', f'{str(hlcDir)}/venv'])
594+
subprocess.run([f'{str(hlcDir)}/venv/bin/pip', 'install', '-r', f'{str(hlcDir)}/requirements.txt', '--no-cache-dir'])
590595

591596
import yaml
592597

593598
try:
594-
hlcConfig = yaml.safe_load(hlcConfigFilePath.read_text())
599+
hlcConfig = yaml.safe_load(hlcConfigTemplatePath.read_text())
595600
except yaml.YAMLError as e:
596-
self._logger.logFatal(f'Failed loading HLC configurations: {e}')
597-
else:
598-
hlcConfig['engine'] = 'projectalice'
599-
hlcConfig['pathToConfig'] = f'/home/{getpass.getuser()}/ProjectAlice/config.json'
600-
hlcConfig['pattern'] = 'projectalice'
601-
hlcConfig['enableDoA'] = False
601+
self._logger.logWarning(f'Failed loading HLC configurations - creating new: {e}')
602+
hlcConfig = dict()
603+
604+
hlcConfig['engine'] = 'projectalice'
605+
hlcConfig['pathToConfig'] = f'/home/{getpass.getuser()}/ProjectAlice/config.json'
606+
hlcConfig['pattern'] = 'projectalice'
607+
hlcConfig['enableDoA'] = False
608+
609+
serviceFile = hlcDistributedServiceFilePath.read_text()
610+
serviceFile = serviceFile.replace('%WORKING_DIR%', f'{str(hlcDir)}')
611+
serviceFile = serviceFile.replace('%EXECSTART%', f'WorkingDirectory={str(hlcDir)}/venv/bin/python main.py --hermesLedControlConfig=/home/{getpass.getuser()}/.config/HermesLedControl/configuration.yml')
612+
serviceFile = serviceFile.replace('%USER%', f'User={getpass.getuser()}')
613+
hlcDistributedServiceFilePath.write_text(serviceFile)
614+
subprocess.run(['sudo', 'cp', hlcDistributedServiceFilePath, hlcServiceFilePath])
615+
602616

603617
useFallbackHLC = False
604618
if initConfs['installSound']:

core/ProjectAlice.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def __init__(self, restartHandler: callable):
6868

6969
def checkDependencies(self) -> bool:
7070
"""
71-
Compares .hash files against requirements.txt and sysrrequirement.txt. Updates dependencies if necessary
71+
Compares .hash files against requirements.txt and sysrequirement.txt. Updates dependencies if necessary
7272
:return: boolean False if the check failed, new deps were installed (reboot maybe? :) )
7373
"""
7474
HASH_SUFFIX = '.hash'
@@ -173,7 +173,7 @@ def updateProjectAlice(self):
173173
self._superManager.stateManager.setState(STATE, newState=StateType.RUNNING)
174174

175175
self._isUpdating = True
176-
req = requests.get(url=f'{constants.GITHUB_API_URL}/ProjectAlice/branches', auth=SuperManager.getInstance().configManager.getGithubAuth())
176+
req = requests.get(url=f'{constants.GITHUB_API_URL}/ProjectAlice/branches', auth=SuperManager.getInstance().configManager.githubAuth)
177177
if req.status_code != 200:
178178
self._logger.logWarning('Failed checking for updates')
179179
self._superManager.stateManager.setState(STATE, newState=StateType.ERROR)

core/ProjectAliceExceptions.py

+16-2
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,25 @@ def __init__(self, clazz: str, funcName: str):
5656

5757
class SkillStartingFailed(ProjectAliceException):
5858

59-
def __init__(self, skillName: str = '', error: str = ''):
59+
def __init__(self, skillName: str, error: str = ''):
6060
super().__init__(message=error)
6161
self._logger.logWarning(f'[{skillName}] Error starting skill: {error}')
6262

63-
if skillName:
63+
if skillName in SuperManager.getInstance().skillManager.NEEDED_SKILLS:
64+
self._logger.logFatal(f'Skill **{skillName}** is required to continue, sorry')
65+
else:
66+
SuperManager.getInstance().skillManager.deactivateSkill(skillName)
67+
68+
69+
class SkillInstanceFailed(ProjectAliceException):
70+
71+
def __init__(self, skillName: str, error: str = ''):
72+
super().__init__(message=error)
73+
self._logger.logWarning(f'[{skillName}] Error creating skill instance: {error}')
74+
75+
if skillName in SuperManager.getInstance().skillManager.NEEDED_SKILLS:
76+
self._logger.logFatal(f'Skill **{skillName}** is required to continue, sorry')
77+
else:
6478
SuperManager.getInstance().skillManager.deactivateSkill(skillName)
6579

6680

core/asr/ASRManager.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ def _startASREngine(self, forceAsr=None):
116116
self.logFatal("Couldn't start any ASR, going down")
117117
return
118118

119-
self.logWarning(f'Something went wrong starting user ASR, falling back to **{fallback}**: {e}')
119+
self.logWarning(f'Something went wrong starting user ASR, falling back to **{fallback}**: {e}', printStack=True)
120120
self._startASREngine(forceAsr=fallback)
121121

122122

core/asr/model/ASRResult.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424

2525
@dataclass
26-
class ASRResult:
26+
class ASRResult(object):
2727
text: str
2828
session: DialogSession
2929
likelihood: float

0 commit comments

Comments
 (0)