def __init__(self, host: str, port: int, hotwordName: str): super().__init__() self._logger = Logger(prepend='[HotwordDownloadThread]') self._host = host self._port = int(port) self._hotwordName = hotwordName self.setDaemon(True)
def onStop(self): mqttManager = self._managers.pop( 'MqttManager', None) # Mqtt goes down last with bug reporter bugReportManager = self._managers.pop( 'BugReportManager', None) # bug reporter goes down as last skillManager = self._managers.pop( 'SkillManager', None) # Skill manager goes down first, to tell the skills if skillManager: try: skillManager.onStop() except Exception as e: Logger().logError(f'Error stopping SkillManager: {e}') for managerName, manager in self._managers.items(): try: if manager.isActive: manager.onStop() except Exception as e: Logger().logError( f'Error while shutting down manager **{managerName}**: {e}' ) if mqttManager: try: mqttManager.onStop() except Exception as e: Logger().logError(f'Error stopping MqttManager: {e}') if bugReportManager: try: bugReportManager.onStop() except Exception as e: Logger().logError(f'Error stopping BugReportManager: {e}')
def __init__(self, restartHandler: callable): Singleton.__init__(self, self.NAME) self._logger = Logger(prepend='[Project Alice]') self._logger.logInfo('Starting Alice main unit') self._booted = False self._isUpdating = False self._shuttingDown = False self._restart = False self._restartHandler = restartHandler if not self.checkDependencies(): self._restart = True self._restartHandler() else: with Stopwatch() as stopWatch: self._superManager = SuperManager(self) self._superManager.initManagers() self._superManager.onStart() if self._superManager.configManager.getAliceConfigByName( 'useHLC'): self._superManager.commons.runRootSystemCommand( ['systemctl', 'start', 'hermesledcontrol']) self._superManager.onBooted() self._logger.logInfo(f'Started in {stopWatch} seconds') self._booted = True
def __init__(self, host: str, port: int, zipPath: str): super().__init__() self._logger = Logger() self.setDaemon(True) self._host = host self._port = port self._zipPath = Path(zipPath)
def __init__(self, host: str, port: int, zipPath: str): super().__init__() self._logger = Logger(prepend='[HotwordUploadThread]') self.setDaemon(True) self._host = host self._port = port self._zipPath = Path(zipPath)
class State: name: str currentState: StateType = StateType.BORN logger: Logger = Logger(prepend='[State]') callbacks: list = field(default_factory=list) def subscribe(self, callback: Callable): self.callbacks.append(callback) def unsubscribe(self, callback: Callable): self.callbacks.remove(callback) def setState(self, newState: StateType): oldState = self.currentState self.currentState = newState for callback in self.callbacks: try: callback(oldState, newState) except: self.logger.logWarning( f'Failed callback for state {self.name}') def __repr__(self) -> str: return f'State "{self.name}" Current state "{self.currentState.value}"'
def __init__(self, name): super().__init__() if self.INSTANCE: Logger().logFatal(f'Trying to instanciate {name} but instance already exists') raise KeyboardInterrupt else: self.INSTANCE = self
def onStop(self): managerName = constants.UNKNOWN_MANAGER try: for managerName, manager in self._managers.items(): manager.onStop() except Exception as e: Logger().logError( f'Error while shutting down manager "{managerName}": {e}')
def onStop(self): managerName = constants.UNKNOWN_MANAGER try: mqttManager = self._managers.pop('MqttManager') for managerName, manager in self._managers.items(): manager.onStop() managerName = mqttManager.name mqttManager.onStop() except KeyError as e: Logger().logWarning( f'Manager **{managerName}** was not running: {e}') except Exception as e: Logger().logError( f'Error while shutting down manager **{managerName}**: {e}') traceback.print_exc()
def __init__(self, message: str = None, status: int = None, context: list = None): self._logger = Logger() self._message = message self._status = status self._context = context super().__init__(message)
def restartManager(self, manager: str): if not manager in self._managers: Logger().logWarning( f'Was asking to restart manager **{manager}** but it doesn\'t exist' ) return self._managers[manager].onStop() self._managers[manager].onStart() self._managers[manager].onBooted()
def exceptionDecorator(*args, **kwargs): try: return func(*args, **kwargs) except exceptions as e: Logger(depth=6).logWarning(msg=e, printStack=printStack) return _exceptHandler(*args, text=text, exceptHandler=exceptHandler, returnText=returnText, **kwargs)
def __init__(self, restartHandler: callable): Singleton.__init__(self, self.NAME) self._logger = Logger() self._logger.logInfo('Starting up Project Alice') self._booted = False with Stopwatch() as stopWatch: self._restart = False self._restartHandler = restartHandler self._superManager = SuperManager(self) self._superManager.initManagers() self._superManager.onStart() if self._superManager.configManager.getAliceConfigByName('useHLC'): self._superManager.commons.runRootSystemCommand( ['systemctl', 'start', 'hermesledcontrol']) self._superManager.onBooted() self._logger.logInfo(f'- Started Project Alice in {stopWatch} seconds') self._booted = True
def onBooted(self): self.mqttManager.playSound(soundFilename='boot') manager = None try: for manager in self._managers.values(): if manager: manager.onBooted() except Exception as e: Logger().logError( f'Error while sending onBooted to manager **{manager.name}**: {e}' )
def onStop(self): managerName = constants.UNKNOWN_MANAGER try: mqttManager = self._managers.pop('MqttManager') for managerName, manager in self._managers.items(): manager.onStop() managerName = mqttManager.name mqttManager.onStop() except Exception as e: Logger().logError( f'Error while shutting down manager **{managerName}**: {e}')
def settingDecorator(*args, **kwargs): if not settingName: Logger().logWarning(msg='Cannot use IfSetting decorator without settingName') return None configManager = SuperManager.getInstance().configManager value = configManager.getSkillConfigByName(skillName, settingName) if skillName else configManager.getAliceConfigByName(settingName) if value is None: return None if (not inverted and value == settingValue) or \ (inverted and value != settingValue): return func(*args, **kwargs)
def onBooted(self): manager = None try: for manager in self._managers.values(): if manager: manager.onBooted() except Exception as e: Logger().logError( f'Error while sending onBooted to manager **{manager.name}**: {e}' ) deviceList = self.deviceManager.getDevicesWithAbilities( [DeviceAbility.IS_SATELITTE, DeviceAbility.IS_CORE]) self.mqttManager.playSound(soundFilename='boot', deviceUid=deviceList)
class ProjectAlice(Singleton): NAME = 'ProjectAlice' def __init__(self, restartHandler: callable): Singleton.__init__(self, self.NAME) self._logger = Logger(prepend='[Project Alice]') self._logger.logInfo('Starting Alice main unit') self._booted = False self._isUpdating = False self._shuttingDown = False self._restart = False self._restartHandler = restartHandler if not self.checkDependencies(): self._restart = True self._restartHandler() else: with Stopwatch() as stopWatch: self._superManager = SuperManager(self) self._superManager.initManagers() self._superManager.onStart() if self._superManager.configManager.getAliceConfigByName( 'useHLC'): self._superManager.commons.runRootSystemCommand( ['systemctl', 'start', 'hermesledcontrol']) self._superManager.onBooted() self._logger.logInfo(f'Started in {stopWatch} seconds') self._booted = True def checkDependencies(self) -> bool: """ Compares .hash files against requirements.txt and sysrequirement.txt. Updates dependencies if necessary :return: boolean False if the check failed, new deps were installed (reboot maybe? :) ) """ HASH_SUFFIX = '.hash' TXT_SUFFIX = '.txt' path = Path('requirements') savedHash = path.with_suffix(HASH_SUFFIX) reqHash = hashlib.blake2b( path.with_suffix(TXT_SUFFIX).read_bytes()).hexdigest() if not savedHash.exists() or savedHash.read_text() != reqHash: self._logger.logInfo( 'Pip dependencies added or removed, updating virtual environment' ) subprocess.run([ './venv/bin/pip', 'install', '-r', str(path.with_suffix(TXT_SUFFIX)) ]) savedHash.write_text(reqHash) return False path = Path('sysrequirements') savedHash = path.with_suffix(HASH_SUFFIX) reqHash = hashlib.blake2b( path.with_suffix(TXT_SUFFIX).read_bytes()).hexdigest() if not savedHash.exists() or savedHash.read_text() != reqHash: self._logger.logInfo( 'System dependencies added or removed, updating system') reqs = [ line.rstrip('\n') for line in open(path.with_suffix(TXT_SUFFIX)) ] subprocess.run([ 'sudo', 'apt-get', 'install', '-y', '--allow-unauthenticated' ] + reqs) savedHash.write_text(reqHash) return False path = Path('pipuninstalls') savedHash = path.with_suffix(HASH_SUFFIX) reqHash = hashlib.blake2b( path.with_suffix(TXT_SUFFIX).read_bytes()).hexdigest() if not savedHash.exists() or savedHash.read_text() != reqHash: self._logger.logInfo( 'Pip conflicting dependencies added, updating virtual environment' ) subprocess.run([ './venv/bin/pip', 'uninstall', '-y', '-r', str(path.with_suffix(TXT_SUFFIX)) ]) savedHash.write_text(reqHash) return False return True @property def name(self) -> str: # NOSONAR return self.NAME @property def isBooted(self) -> bool: return self._booted @property def restart(self) -> bool: return self._restart @restart.setter def restart(self, value: bool): self._restart = value def doRestart(self): self._restart = True self.onStop() def onStop(self, withReboot: bool = False): self._logger.logInfo('Shutting down') self._shuttingDown = True self._superManager.onStop() if self._superManager.configManager.getAliceConfigByName('useHLC'): self._superManager.commons.runRootSystemCommand( ['systemctl', 'stop', 'hermesledcontrol']) self._booted = False self.INSTANCE = None if withReboot: subprocess.run(['sudo', 'shutdown', '-r', 'now']) else: self._restartHandler() def wipeAll(self): # Set as restarting so skills don't install / update self._restart = True self._superManager.skillManager.wipeSkills() self._superManager.databaseManager.clearDB() self._superManager.assistantManager.clearAssistant() self._superManager.dialogTemplateManager.clearCache(False) self._superManager.nluManager.clearCache() def updateProjectAlice(self): self._logger.logInfo('Checking for core updates') STATE = 'projectalice.core.updating' state = self._superManager.stateManager.getState(STATE) if not state: self._superManager.stateManager.register( STATE, initialState=StateType.RUNNING) elif state.currentState == StateType.RUNNING: self._logger.logInfo('Update cancelled, already running') return self._superManager.stateManager.setState(STATE, newState=StateType.RUNNING) self._isUpdating = True req = requests.get( url=f'{constants.GITHUB_API_URL}/ProjectAlice/branches', auth=SuperManager.getInstance().configManager.githubAuth) if req.status_code != 200: self._logger.logWarning('Failed checking for updates') self._superManager.stateManager.setState(STATE, newState=StateType.ERROR) return userUpdatePref = SuperManager.getInstance( ).configManager.getAliceConfigByName('aliceUpdateChannel') if userUpdatePref == 'master': candidate = 'master' else: candidate = Version.fromString(constants.VERSION) for branch in req.json(): if 'dependabot' in branch['name']: continue repoVersion = Version.fromString(branch['name']) if not repoVersion.isVersionNumber: continue releaseType = repoVersion.releaseType if userUpdatePref == 'rc' and releaseType in { 'b', 'a' } or userUpdatePref == 'beta' and releaseType == 'a': continue if repoVersion > candidate: candidate = repoVersion self._logger.logInfo(f'Checking on "{str(candidate)}" update channel') commons = SuperManager.getInstance().commons currentHash = subprocess.check_output( ['git', 'rev-parse', '--short HEAD']) commons.runSystemCommand(['git', '-C', commons.rootDir(), 'stash']) commons.runSystemCommand( ['git', '-C', commons.rootDir(), 'clean', '-df']) commons.runSystemCommand( ['git', '-C', commons.rootDir(), 'checkout', str(candidate)]) commons.runSystemCommand(['git', '-C', commons.rootDir(), 'pull']) commons.runSystemCommand( ['git', '-C', commons.rootDir(), 'submodule', 'init']) commons.runSystemCommand( ['git', '-C', commons.rootDir(), 'submodule', 'update']) commons.runSystemCommand([ 'git', '-C', commons.rootDir(), 'submodule', 'foreach', 'git', 'checkout', f'builds_{str(candidate)}' ]) commons.runSystemCommand([ 'git', '-C', commons.rootDir(), 'submodule', 'foreach', 'git', 'pull' ]) newHash = subprocess.check_output(['git', 'rev-parse', '--short HEAD']) # Remove install tickets [ file.unlink() for file in Path( commons.rootDir(), 'system/skillInstallTickets').glob('*') if file.is_file() ] self._superManager.stateManager.setState(STATE, newState=StateType.FINISHED) if currentHash != newHash: self._logger.logWarning( 'New Alice version installed, need to restart...') self._superManager.webUINotificationManager.newNotification( typ=UINotificationType.INFO, notification='aliceUpdated') self.doRestart() self._logger.logInfo('Update checks completed.') self._isUpdating = False @property def updating(self) -> bool: return self._isUpdating @property def shuttingDown(self) -> bool: return self._shuttingDown
def onStart(self): try: commons = self._managers.pop('CommonsManager') commons.onStart() configManager = self._managers.pop('ConfigManager') configManager.onStart() languageManager = self._managers.pop('LanguageManager') languageManager.onStart() locationManager = self._managers.pop('LocationManager') locationManager.onStart() deviceManager = self._managers.pop('DeviceManager') deviceManager.onStart() audioServer = self._managers.pop('AudioManager') audioServer.onStart() internetManager = self._managers.pop('InternetManager') internetManager.onStart() databaseManager = self._managers.pop('DatabaseManager') databaseManager.onStart() userManager = self._managers.pop('UserManager') userManager.onStart() mqttManager = self._managers.pop('MqttManager') mqttManager.onStart() talkManager = self._managers.pop('TalkManager') skillManager = self._managers.pop('SkillManager') assistantManager = self._managers.pop('AssistantManager') dialogTemplateManager = self._managers.pop('DialogTemplateManager') nluManager = self._managers.pop('NluManager') nodeRedManager = self._managers.pop('NodeRedManager') for manager in self._managers.values(): if manager: manager.onStart() talkManager.onStart() nluManager.onStart() skillManager.onStart() dialogTemplateManager.onStart() assistantManager.onStart() nodeRedManager.onStart() self._managers[configManager.name] = configManager self._managers[audioServer.name] = audioServer self._managers[languageManager.name] = languageManager self._managers[locationManager.name] = locationManager self._managers[deviceManager.name] = deviceManager self._managers[talkManager.name] = talkManager self._managers[databaseManager.name] = databaseManager self._managers[userManager.name] = userManager self._managers[mqttManager.name] = mqttManager self._managers[skillManager.name] = skillManager self._managers[dialogTemplateManager.name] = dialogTemplateManager self._managers[assistantManager.name] = assistantManager self._managers[nluManager.name] = nluManager self._managers[internetManager.name] = internetManager self._managers[nodeRedManager.name] = nodeRedManager except Exception as e: import traceback traceback.print_exc() Logger().logFatal(f'Error while starting managers: {e}')
class ProjectAliceObject: def __init__(self, logDepth: int = 3, *args, **kwargs): self._depth = logDepth self._logger = Logger(logDepth) def __repr__(self): return json.dumps(self.__dict__) def __str__(self): return json.dumps(self.__dict__) def broadcast(self, method: str, exceptions: list = None, manager=None, propagateToSkills: bool = False, **kwargs): if not exceptions: exceptions = list() if isinstance(exceptions, str): exceptions = [exceptions] if not exceptions and not manager: # Prevent infinite loop of broadcaster being broadcasted to re broadcasting self.logWarning( 'Cannot broadcast to itself, the calling method has to be put in exceptions' ) return if 'ProjectAlice' not in exceptions: exceptions.append('ProjectAlice') if not method.startswith('on'): method = f'on{method[0].capitalize() + method[1:]}' deadManagers = list() for name, man in SM.SuperManager.getInstance().managers.items(): if not man: deadManagers.append(name) continue if (manager and man.name != manager.name) or man.name in exceptions: continue try: func = getattr(man, method, None) if func: func(**kwargs) except TypeError as e: self.logWarning( f'- Failed to broadcast event {method} to {man.name}: {e}') if propagateToSkills: self.SkillManager.skillBroadcast(method=method, **kwargs) for name in deadManagers: del SM.SuperManager.getInstance().managers[name] def logInfo(self, msg: str): self._logger.doLog(function='info', msg=msg, printStack=False) def logError(self, msg: str): self._logger.doLog(function='error', msg=msg) def logDebug(self, msg: str): self._logger.doLog(function='debug', msg=msg, printStack=False) def logFatal(self, msg: str): self._logger.doLog(function='fatal', msg=msg) try: self.ProjectAlice.onStop() except: exit() def logWarning(self, msg: str, printStack: bool = False): self._logger.doLog(function='warning', msg=msg, printStack=printStack) def logCritical(self, msg: str): self._logger.doLog(function='critical', msg=msg) def onStart(self): pass def onStop(self): pass def onBooted(self): pass def onSkillInstalled(self, skill: str): pass def onSkillUpdated(self, skill: str): pass def onInternetConnected(self): pass def onInternetLost(self): pass def onHotword(self, siteId: str, user: str = constants.UNKNOWN_USER): pass def onHotwordToggleOn(self, siteId: str): pass def onSessionStarted(self, session): pass def onStartListening(self, session): pass def onStopListening(self, session): pass def onCaptured(self, session): pass def onNluQuery(self, session): pass def onIntentParsed(self, session): pass def onUserCancel(self, session): pass def onSessionTimeout(self, session): pass def onIntentNotRecognized(self, session): pass def onSessionError(self, session): pass def onSessionEnded(self, session): pass def onSay(self, session): pass def onSayFinished(self, session): pass def onSessionQueued(self, session): pass def onMessage(self, session) -> bool: """ Do not consume the intent by default """ return False def onSleep(self): pass def onWakeup(self): pass def onGoingBed(self): pass def onLeavingHome(self): pass def onReturningHome(self): pass def onEating(self): pass def onWatchingTV(self): pass def onCooking(self): pass def onMakeup(self): pass def onContextSensitiveDelete(self, sessionId: str): pass def onContextSensitiveEdit(self, sessionId: str): pass def onFullMinute(self): pass def onFiveMinute(self): pass def onQuarterHour(self): pass def onFullHour(self): pass def onWakeword(self, siteId: str, user: str = constants.UNKNOWN_USER): pass def onMotionDetected(self): pass def onMotionStopped(self): pass def onButtonPressed(self): pass def onButtonReleased(self): pass def onDeviceConnecting(self): pass def onDeviceDisconnecting(self): pass def onUVIndexAlert(self, *args, **kwargs): pass def onRaining(self, *args, **kwargs): pass def onTooMuchRain(self, *args, **kwargs): pass def onWindy(self, *args, **kwargs): pass def onFreezing(self, *args, **kwargs): pass def onTemperatureHighAlert(self, *args, **kwargs): pass def onTemperatureLowAlert(self, *args, **kwargs): pass def onCO2Alert(self, *args, **kwargs): pass def onHumidityHighAlert(self, *args, **kwargs): pass def onHumidityLowAlert(self, *args, **kwargs): pass def onNoiseAlert(self, *args, **kwargs): pass def onPressureHighAlert(self, *args, **kwargs): pass def onPressureLowAlert(self, *args, **kwargs): pass def onBroadcastingForNewDeviceStart(self, session): pass def onBroadcastingForNewDeviceStop(self): pass def onAuthenticated(self, session): pass def onAuthenticationFailed(self, session): pass def onAudioFrame(self, **kwargs): pass def onSnipsAssistantInstalled(self, **kwargs): pass def onSnipsAssistantFailedTraining(self, **kwargs): pass def onSkillInstallFailed(self, skill: str): pass def onNluTrained(self, **kwargs): pass def onPartialTextCaptured(self, session, text: str, likelihood: float, seconds: float): pass @property def ProjectAlice(self): return SM.SuperManager.getInstance().projectAlice @property def ConfigManager(self): return SM.SuperManager.getInstance().configManager @property def SkillManager(self): return SM.SuperManager.getInstance().skillManager @property def DeviceManager(self): return SM.SuperManager.getInstance().deviceManager @property def DialogSessionManager(self): return SM.SuperManager.getInstance().dialogSessionManager @property def MultiIntentManager(self): return SM.SuperManager.getInstance().multiIntentManager @property def ProtectedIntentManager(self): return SM.SuperManager.getInstance().protectedIntentManager @property def MqttManager(self): return SM.SuperManager.getInstance().mqttManager @property def SnipsServicesManager(self): return SM.SuperManager.getInstance().snipsServicesManager @property def UserManager(self): return SM.SuperManager.getInstance().userManager @property def DatabaseManager(self): return SM.SuperManager.getInstance().databaseManager @property def InternetManager(self): return SM.SuperManager.getInstance().internetManager @property def TelemetryManager(self): return SM.SuperManager.getInstance().telemetryManager @property def ThreadManager(self): return SM.SuperManager.getInstance().threadManager @property def TimeManager(self): return SM.SuperManager.getInstance().timeManager @property def ASRManager(self): return SM.SuperManager.getInstance().asrManager @property def LanguageManager(self): return SM.SuperManager.getInstance().languageManager @property def TalkManager(self): return SM.SuperManager.getInstance().talkManager @property def TTSManager(self): return SM.SuperManager.getInstance().ttsManager @property def WakewordManager(self): return SM.SuperManager.getInstance().wakewordManager @property def WebInterfaceManager(self): return SM.SuperManager.getInstance().webInterfaceManager @property def Commons(self): return SM.SuperManager.getInstance().commonsManager @property def SnipsWatchManager(self): return SM.SuperManager.getInstance().snipsWatchManager @property def SkillStoreManager(self): return SM.SuperManager.getInstance().skillStoreManager @property def NluManager(self): return SM.SuperManager.getInstance().nluManager @property def DialogTemplateManager(self): return SM.SuperManager.getInstance().dialogTemplateManager @property def SnipsAssistantManager(self): return SM.SuperManager.getInstance().snipsAssistantManager
class HotwordDownloadThread(Thread): def __init__(self, host: str, port: int, hotwordName: str): super().__init__() self._logger = Logger(prepend='[HotwordDownloadThread]') self._host = host self._port = int(port) self._hotwordName = hotwordName self.setDaemon(True) def run(self): sock = None try: self._logger.logInfo('Cleaning up') rootPath = Path(SuperManager.getInstance().commons.rootDir(), 'hotwords') hotwordPath = rootPath / f'{self._hotwordName}' zipPath = hotwordPath.with_suffix('.zip') if zipPath.exists(): zipPath.unlink() if hotwordPath.exists(): shutil.rmtree(hotwordPath, ignore_errors=True) self._logger.logInfo( f'Connecting to **{self._host}_{self._port}**') sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((self._host, self._port)) self._logger.logInfo( f'Receiving hotword package: **{self._hotwordName}**') sock.settimeout(2) try: with zipPath.open('wb') as f: while True: data = sock.recv(1024) if not data: break f.write(data) except socket.timeout: sock.settimeout(None) except Exception as e: self._logger.logError(f'Error downloading hotword: {e}') if sock: sock.send(b'-1') sock.close() return try: self._logger.logInfo('New hotword received, unpacking...') shutil.unpack_archive(filename=zipPath, extract_dir=hotwordPath) conf = SuperManager.getInstance( ).configManager.getSnipsConfiguration(parent='snips-hotword', key='model', createIfNotExist=True) if not isinstance(conf, list): conf = list() addSnips = True regex = re.compile(f'.*/{hotwordPath}=[0-9.]+$') copy = conf.copy() for i, hotword in enumerate(copy): if hotword.find('/snips_hotword='): addSnips = False elif regex.match(hotword): conf.pop(i) if addSnips: conf.append(str(rootPath / 'snips_hotword=0.53')) conf.append(f'{hotwordPath}=0.52') SuperManager.getInstance().configManager.updateSnipsConfiguration( parent='snips-hotword', key='model', value=conf, createIfNotExist=True) subprocess.run(['sudo', 'systemctl', 'restart', 'snips-satellite']) sock.send(b'0') self._logger.logInfo( f'Sucessfully installed new hotword **{self._hotwordName}**') except Exception as e: self._logger.logError( f'Error while unpacking and installing hotword: {e}') sock.send(b'-2') finally: sock.close() os.remove(zipPath)
def __init__(self, *args, **kwargs): self._logger = Logger(*args, **kwargs)
class ProjectAliceObject: def __init__(self, *args, **kwargs): self._logger = Logger(*args, **kwargs) def __repr__(self) -> str: ret = copy(self.__dict__) ret.pop('_logger') return json.dumps(ret) def __str__(self) -> str: return self.__repr__() def broadcast(self, method: str, exceptions: list = None, manager=None, **kwargs): if not exceptions: exceptions = list() if isinstance(exceptions, str): exceptions = [exceptions] if not exceptions and not manager: # Prevent infinite loop of broadcaster being broadcasted to re broadcasting self.logWarning( 'Cannot broadcast to itself, the calling method has to be put in exceptions' ) return if 'ProjectAlice' not in exceptions: exceptions.append('ProjectAlice') if not method.startswith('on'): method = f'on{method[0].capitalize() + method[1:]}' deadManagers = list() for name, man in SM.SuperManager.getInstance().managers.items(): if not man: deadManagers.append(name) continue if (manager and man.name != manager.name) or man.name in exceptions: continue try: func = getattr(man, method, None) if func: func(**kwargs) except TypeError as e: self.logWarning( f'- Failed to broadcast event {method} to {man.name}: {e}') for name in deadManagers: del SM.SuperManager.getInstance().managers[name] def logInfo(self, msg: str): self._logger.doLog(function='info', msg=self.decorateLogs(msg), printStack=False) def logError(self, msg: str): self._logger.doLog(function='error', msg=self.decorateLogs(msg)) def logDebug(self, msg: str): self._logger.doLog(function='debug', msg=self.decorateLogs(msg), printStack=False) def logFatal(self, msg: str): self._logger.doLog(function='fatal', msg=self.decorateLogs(msg)) try: self.ProjectAlice.onStop() exit() except: exit() def logWarning(self, msg: str, printStack: bool = False): self._logger.doLog(function='warning', msg=self.decorateLogs(msg), printStack=printStack) def logCritical(self, msg: str): self._logger.doLog(function='critical', msg=self.decorateLogs(msg)) def decorateLogs(self, text: str) -> str: return f'[{self.__class__.__name__}] {text}' def onStart(self): pass def onStop(self): pass def onBooted(self): pass def onFullMinute(self): pass def onFiveMinute(self): pass def onQuarterHour(self): pass def onFullHour(self): pass @property def ProjectAlice(self): return SM.SuperManager.getInstance().projectAlice @property def ConfigManager(self): return SM.SuperManager.getInstance().configManager @property def MqttManager(self): return SM.SuperManager.getInstance().mqttManager @property def DatabaseManager(self): return SM.SuperManager.getInstance().databaseManager @property def ThreadManager(self): return SM.SuperManager.getInstance().threadManager @property def TimeManager(self): return SM.SuperManager.getInstance().timeManager @property def HotwordManager(self): return SM.SuperManager.getInstance().hotwordManager @property def Commons(self): return SM.SuperManager.getInstance().commonsManager @property def NetworkManager(self): return SM.SuperManager.getInstance().networkManager
class ProjectAliceObject(object): DEPENDENCIES = {'internal': {}, 'external': {}, 'system': [], 'pip': []} def __init__(self, *args, **kwargs): self._logger = Logger(*args, **kwargs) def __repr__(self) -> str: ret = copy(self.__dict__) ret.pop('_logger') return json.dumps(ret) def __str__(self) -> str: return self.__repr__() def broadcast(self, method: str, exceptions: list = None, manager=None, propagateToSkills: bool = False, **kwargs): # NOSONAR if not exceptions: exceptions = list() if isinstance(exceptions, str): exceptions = [exceptions] if not exceptions and not manager: # Prevent infinite loop of broadcaster being broadcasted to re broadcasting self.logWarning( 'Cannot broadcast to itself, the calling method has to be put in exceptions' ) return if 'ProjectAlice' not in exceptions: exceptions.append('ProjectAlice') if 'DialogManager' not in exceptions: exceptions.append('DialogManager') if not method.startswith('on'): method = f'on{method[0].capitalize() + method[1:]}' # Give absolute priority to DialogManager try: func = getattr( SM.SuperManager.getInstance().getManager('DialogManager'), method, None) if func: func(**kwargs) except TypeError as e: self.logWarning( f'Failed to broadcast event **{method}** to **DialogManager**: {e}' ) deadManagers = list() for name, man in SM.SuperManager.getInstance().managers.copy().items(): if not man: deadManagers.append(name) continue if (manager and man.name != manager.name) or man.name in exceptions: continue try: func = getattr(man, method, None) if func: func(**kwargs) except TypeError as e: self.logWarning( f'Failed to broadcast event **{method}** to **{man.name}**: {e}' ) if propagateToSkills: self.SkillManager.skillBroadcast(method=method, **kwargs) for name in deadManagers: del SM.SuperManager.getInstance().managers[name] if method == 'onAudioFrame': return # Now send the event over mqtt payload = dict() for item, value in kwargs.items(): try: payload[item] = json.dumps(value) except: # Cannot serialize that attribute, do nothing pass self.MqttManager.publish(topic=f'projectalice/events/{method}', payload=payload) def checkDependencies(self) -> bool: self.logInfo('Checking dependencies') for dep in { **self.DEPENDENCIES.get('internal', dict()), **self.DEPENDENCIES.get('external', dict()) }: result = self.Commons.runRootSystemCommand( ['dpkg-query', '-l', dep]) if result.returncode: self.logWarning(f'Found missing dependency: {dep}') return False for dep in self.DEPENDENCIES['pip']: match = re.match(r'^([a-zA-Z0-9-_]*)(?:([=><]{0,2})([\d.]*)$)', dep) if not match: continue packageName, operator, version = match.groups() if not packageName: self.logWarning('Wrongly declared PIP requirement') continue try: installedVersion = packageVersion(packageName) except PackageNotFoundError: self.logWarning(f'Found missing dependencies: {packageName}') return False if not installedVersion or not operator or not version: continue version = Version.fromString(version) installedVersion = Version.fromString(installedVersion) if (operator == '==' and version != installedVersion) or \ (operator == '>=' and installedVersion < version) or \ (operator == '>' and (installedVersion < version or installedVersion == version)) or \ (operator == '<' and (installedVersion > version or installedVersion == version)): self.logWarning( f'Dependency "{packageName}" is not conform with version requirements' ) return False for dep in self.DEPENDENCIES['system']: result = self.Commons.runRootSystemCommand( ['dpkg-query', '-l', dep]) if result.returncode: self.logWarning(f'Found missing dependency: {dep}') return False return True def installDependencies(self) -> bool: self.logInfo('Installing dependencies') try: for dep, link in self.DEPENDENCIES.get('internal', dict()).items(): self.logInfo(f'Installing "{dep}"') result = self.Commons.runRootSystemCommand( ['apt-get', 'install', '-y', f'./{link}']) if result.returncode: raise Exception(result.stderr) self.logInfo(f'Installed!') for dep, link in self.DEPENDENCIES.get('external', dict()).items(): self.logInfo(f'Downloading "{dep}"') if not self.Commons.downloadFile(link, link.rsplit('/')[-1]): return False self.logInfo(f'Installing "{dep}"') result = self.Commons.runRootSystemCommand( ['apt-get', 'install', '-y', f'./{link.rsplit("/")[-1]}']) if result.returncode: raise Exception(result.stderr) Path(link.rsplit('/')[-1]).unlink() self.logInfo(f'Installed!') for dep in self.DEPENDENCIES['system']: self.logInfo(f'Installing "{dep}"') result = self.Commons.runRootSystemCommand( ['apt-get', 'install', '-y', dep]) if result.returncode: raise Exception(result.stderr) self.logInfo(f'Installed!') for dep in self.DEPENDENCIES['pip']: self.logInfo(f'Installing "{dep}"') result = self.Commons.runSystemCommand( ['./venv/bin/pip', 'install', dep]) if result.returncode: raise Exception(result.stderr) self.logInfo(f'Installed!') return True except Exception as e: self.logError(f'Installing dependencies failed: {e}') return False def logInfo(self, msg: str, plural: Union[list, str] = None): self._logger.doLog(function='info', msg=self.decorateLogs(msg), printStack=False, plural=plural) def logError(self, msg: str, plural: Union[list, str] = None): self._logger.doLog(function='error', msg=self.decorateLogs(msg), plural=plural) def logDebug(self, msg: str, plural: Union[list, str] = None): self._logger.doLog(function='debug', msg=self.decorateLogs(msg), printStack=False, plural=plural) def logFatal(self, msg: str, plural: Union[list, str] = None): self._logger.doLog(function='fatal', msg=self.decorateLogs(msg), plural=plural) try: self.ProjectAlice.onStop() except: exit() def logWarning(self, msg: str, printStack: bool = False, plural: Union[list, str] = None): self._logger.doLog(function='warning', msg=self.decorateLogs(msg), printStack=printStack, plural=plural) def logCritical(self, msg: str, plural: Union[list, str] = None): self._logger.doLog(function='critical', msg=self.decorateLogs(msg), plural=plural) def decorateLogs(self, text: str) -> str: return f'[{self.__class__.__name__}] {text}' def onStart(self): pass # Super object function is overridden only if needed def onStop(self, **kwargs): pass # Super object function is overridden only if needed def onBooted(self): pass # Super object function is overridden only if needed def onSkillInstalled(self, skill: str): pass # Super object function is overridden only if needed def onSkillDeleted(self, skill: str): pass # Super object function is overridden only if needed def onSkillUpdated(self, skill: str): pass # Super object function is overridden only if needed def onSkillUpdating(self, skill: str): pass # Super object function is overridden only if needed def onSkillStarted(self, skill: str): pass # Super object function is overridden only if needed def onSkillStopped(self, skill: str): pass # Super object function is overridden only if needed def onSkillActivated(self, skill: str): pass # Super object function is overridden only if needed def onSkillDeactivated(self, skill: str): pass # Super object function is overridden only if needed def onInternetConnected(self): pass # Super object function is overridden only if needed def onInternetLost(self): pass # Super object function is overridden only if needed def onHotword(self, deviceUid: str, user: str = constants.UNKNOWN_USER): pass # Super object function is overridden only if needed def onHotwordToggleOn(self, deviceUid: str, session): pass # Super object function is overridden only if needed def onHotwordToggleOff(self, deviceUid: str, session): pass # Super object function is overridden only if needed def onSessionStarted(self, session): pass # Super object function is overridden only if needed def onContinueSession(self, session): pass # Super object function is overridden only if needed def onAsrToggleOn(self, deviceUid: str): pass # Super object function is overridden only if needed def onAsrToggleOff(self, deviceUid: str): pass # Super object function is overridden only if needed def onStartListening(self, session): pass # Super object function is overridden only if needed def onStopListening(self, session): pass # Super object function is overridden only if needed def onCaptured(self, session): pass # Super object function is overridden only if needed def onNluQuery(self, session): pass # Super object function is overridden only if needed def onIntentParsed(self, session): pass # Super object function is overridden only if needed def onIntent(self, session): pass # Super object function is overridden only if needed def onConfigureIntent(self, intents: list): pass # Super object function is overridden only if needed def onUserCancel(self, session): pass # Super object function is overridden only if needed def onSessionTimeout(self, session): pass # Super object function is overridden only if needed def onIntentNotRecognized(self, session): pass # Super object function is overridden only if needed def onNluIntentNotRecognized(self, session): pass # Super object function is overridden only if needed def onNluError(self, session): pass # Super object function is overridden only if needed def onSessionError(self, session): pass # Super object function is overridden only if needed def onStartSession(self, deviceUid: str, payload: dict): pass # Super object function is overridden only if needed def onSessionEnded(self, session): pass # Super object function is overridden only if needed def onSay(self, session): pass # Super object function is overridden only if needed def onSayFinished(self, session, uid: str = None): pass # Super object function is overridden only if needed def onSessionQueued(self, session): pass # Super object function is overridden only if needed def onMessage(self, session) -> bool: # NOSONAR """ Do not consume the intent by default """ return False def onSleep(self): pass # Super object function is overridden only if needed def onWakeup(self): pass # Super object function is overridden only if needed def onGoingBed(self): pass # Super object function is overridden only if needed def onLeavingHome(self): pass # Super object function is overridden only if needed def onReturningHome(self): pass # Super object function is overridden only if needed def onEating(self): pass # Super object function is overridden only if needed def onWatchingTV(self): pass # Super object function is overridden only if needed def onCooking(self): pass # Super object function is overridden only if needed def onMakeup(self): pass # Super object function is overridden only if needed def onContextSensitiveDelete(self, session): pass # Super object function is overridden only if needed def onContextSensitiveEdit(self, session): pass # Super object function is overridden only if needed def onFullMinute(self): pass # Super object function is overridden only if needed def onFiveMinute(self): pass # Super object function is overridden only if needed def onQuarterHour(self): pass # Super object function is overridden only if needed def onFullHour(self): pass # Super object function is overridden only if needed def onWakeword(self, deviceUid: str, user: str = constants.UNKNOWN_USER): pass # Super object function is overridden only if needed def onMotionDetected(self): pass # Super object function is overridden only if needed def onMotionStopped(self): pass # Super object function is overridden only if needed def onButtonPressed(self): pass # Super object function is overridden only if needed def onButtonReleased(self): pass # Super object function is overridden only if needed def onDeviceDiscovered(self, device, uid: str): pass # Super object function is overridden only if needed def onDeviceAdded(self, device, uid: str): pass # Super object function is overridden only if needed def onDeviceRemoved(self, device, uid: str): pass # Super object function is overridden only if needed def onDeviceConnecting(self): pass # Super object function is overridden only if needed def onDeviceDisconnecting(self): pass # Super object function is overridden only if needed def onUVIndexAlert(self, *args, **kwargs): pass # Super object function is overridden only if needed def onRaining(self, *args, **kwargs): pass # Super object function is overridden only if needed def onTooMuchRain(self, *args, **kwargs): pass # Super object function is overridden only if needed def onWindy(self, *args, **kwargs): pass # Super object function is overridden only if needed def onFreezing(self, *args, **kwargs): pass # Super object function is overridden only if needed def onTemperatureHighAlert(self, *args, **kwargs): pass # Super object function is overridden only if needed def onTemperatureLowAlert(self, *args, **kwargs): pass # Super object function is overridden only if needed def onCOTwoAlert(self, *args, **kwargs): pass # Super object function is overridden only if needed def onHumidityHighAlert(self, *args, **kwargs): pass # Super object function is overridden only if needed def onHumidityLowAlert(self, *args, **kwargs): pass # Super object function is overridden only if needed def onNoiseAlert(self, *args, **kwargs): pass # Super object function is overridden only if needed def onPressureHighAlert(self, *args, **kwargs): pass # Super object function is overridden only if needed def onGasAlert(self, *args, **kwargs): pass # Super object function is overridden only if needed def onPressureLowAlert(self, *args, **kwargs): pass # Super object function is overridden only if needed def onBroadcastingForNewDeviceStart(self): pass # Super object function is overridden only if needed def onBroadcastingForNewDeviceStop(self, *args): pass # Super object function is overridden only if needed def onAuthenticated(self, session): pass # Super object function is overridden only if needed def onAuthenticationFailed(self, session): pass # Super object function is overridden only if needed def onAudioFrame(self, **kwargs): pass # Super object function is overridden only if needed def onAssistantInstalled(self, **kwargs): pass # Super object function is overridden only if needed def onAssistantFailedTraining(self, **kwargs): pass # Super object function is overridden only if needed def onSkillInstallFailed(self, skill: str): pass # Super object function is overridden only if needed def onNluTrained(self, **kwargs): pass # Super object function is overridden only if needed def onVadUp(self, **kwargs): pass # Super object function is overridden only if needed def onVadDown(self, **kwargs): pass # Super object function is overridden only if needed def onPlayBytes(self, payload: bytearray, deviceUid: str, sessionId: str = None): pass # Super object function is overridden only if needed def onPlayBytesFinished(self, deviceUid: str, sessionId: str = None): pass # Super object function is overridden only if needed def onToggleFeedbackOn(self, deviceUid: str): pass # Super object function is overridden only if needed def onToggleFeedbackOff(self, deviceUid: str): pass # Super object function is overridden only if needed def onPartialTextCaptured(self, session, text: str, likelihood: float, seconds: float): pass # Super object function is overridden only if needed def onEndSession(self, session, reason: str = 'nominal'): pass # Super object function is overridden only if needed def onDeviceHeartbeat(self, uid: str, deviceUid: str = None): pass # Super object function is overridden only if needed def onDeviceStatus(self, session): pass # Super object function is overridden only if needed @property def ProjectAlice(self) -> ProjectAlice: # NOSONAR return SM.SuperManager.getInstance().projectAlice @property def ConfigManager(self) -> ConfigManager: # NOSONAR return SM.SuperManager.getInstance().configManager @property def SkillManager(self) -> SkillManager: # NOSONAR return SM.SuperManager.getInstance().skillManager @property def DeviceManager(self) -> DeviceManager: # NOSONAR return SM.SuperManager.getInstance().deviceManager @property def MultiIntentManager(self) -> MultiIntentManager: # NOSONAR return SM.SuperManager.getInstance().multiIntentManager @property def MqttManager(self) -> MqttManager: # NOSONAR return SM.SuperManager.getInstance().mqttManager @property def UserManager(self) -> UserManager: # NOSONAR return SM.SuperManager.getInstance().userManager @property def DatabaseManager(self) -> DatabaseManager: # NOSONAR return SM.SuperManager.getInstance().databaseManager @property def InternetManager(self) -> InternetManager: # NOSONAR return SM.SuperManager.getInstance().internetManager @property def TelemetryManager(self) -> TelemetryManager: # NOSONAR return SM.SuperManager.getInstance().telemetryManager @property def ThreadManager(self) -> ThreadManager: # NOSONAR return SM.SuperManager.getInstance().threadManager @property def TimeManager(self) -> TimeManager: # NOSONAR return SM.SuperManager.getInstance().timeManager @property def ASRManager(self) -> ASRManager: # NOSONAR return SM.SuperManager.getInstance().asrManager @property def LanguageManager(self) -> LanguageManager: # NOSONAR return SM.SuperManager.getInstance().languageManager @property def TalkManager(self) -> TalkManager: # NOSONAR return SM.SuperManager.getInstance().talkManager @property def TTSManager(self) -> TTSManager: # NOSONAR return SM.SuperManager.getInstance().ttsManager @property def WakewordRecorder(self) -> WakewordRecorder: # NOSONAR return SM.SuperManager.getInstance().wakewordRecorder @property def ApiManager(self) -> ApiManager: # NOSONAR return SM.SuperManager.getInstance().apiManager @property def Commons(self) -> CommonsManager: # NOSONAR return SM.SuperManager.getInstance().commonsManager @property def SkillStoreManager(self) -> SkillStoreManager: # NOSONAR return SM.SuperManager.getInstance().skillStoreManager @property def NluManager(self) -> NluManager: # NOSONAR return SM.SuperManager.getInstance().nluManager @property def DialogTemplateManager(self) -> DialogTemplateManager: # NOSONAR return SM.SuperManager.getInstance().dialogTemplateManager @property def AssistantManager(self) -> AssistantManager: # NOSONAR return SM.SuperManager.getInstance().assistantManager @property def AliceWatchManager(self) -> AliceWatchManager: # NOSONAR return SM.SuperManager.getInstance().aliceWatchManager @property def AudioServer(self) -> AudioManager: # NOSONAR return SM.SuperManager.getInstance().audioManager @property def DialogManager(self) -> DialogManager: # NOSONAR return SM.SuperManager.getInstance().dialogManager @property def LocationManager(self) -> LocationManager: # NOSONAR return SM.SuperManager.getInstance().locationManager @property def WakewordManager(self) -> WakewordManager: # NOSONAR return SM.SuperManager.getInstance().wakewordManager @property def NodeRedManager(self) -> NodeRedManager: # NOSONAR return SM.SuperManager.getInstance().nodeRedManager @property def WidgetManager(self) -> WidgetManager: # NOSONAR return SM.SuperManager.getInstance().widgetManager @property def StateManager(self) -> StateManager: # NOSONAR return SM.SuperManager.getInstance().stateManager @property def WebUIManager(self) -> WebUIManager: # NOSONAR return SM.SuperManager.getInstance().webUiManager @property def SubprocessManager(self) -> SubprocessManager: # NOSONAR return SM.SuperManager.getInstance().subprocessManager @property def WebUINotificationManager(self) -> WebUINotificationManager: # NOSONAR return SM.SuperManager.getInstance().webUINotificationManager @property def BugReportManager(self) -> BugReportManager: # NOSONAR return SM.SuperManager.getInstance().bugReportManager
class ProjectAlice(Singleton): NAME = 'ProjectAlice' def __init__(self, restartHandler: callable): Singleton.__init__(self, self.NAME) self._logger = Logger() self._logger.logInfo('Starting up Project Alice') self._booted = False with Stopwatch() as stopWatch: self._restart = False self._restartHandler = restartHandler self._superManager = SuperManager(self) self._superManager.initManagers() self._superManager.onStart() if self._superManager.configManager.getAliceConfigByName('useHLC'): self._superManager.commons.runRootSystemCommand( ['systemctl', 'start', 'hermesledcontrol']) self._superManager.onBooted() self._logger.logInfo(f'- Started Project Alice in {stopWatch} seconds') self._booted = True @property def name(self) -> str: return self.NAME @property def isBooted(self) -> bool: return self._booted @property def restart(self) -> bool: return self._restart @restart.setter def restart(self, value: bool): self._restart = value def doRestart(self): self._restart = True self.onStop() def onStop(self): self._logger.logInfo('Shutting down Project Alice') self._superManager.onStop() if self._superManager.configManager.getAliceConfigByName('useHLC'): self._superManager.commons.runRootSystemCommand( ['systemctl', 'stop', 'hermesledcontrol']) self.INSTANCE = None self._restartHandler() def wipeAll(self): # Set as restarting so skills don't install / update self._restart = True self._superManager.skillManager.wipeSkills() self._superManager.databaseManager.clearDB() self._superManager.dialogTemplateManager.clearCache(False) self._superManager.nluManager.clearCache() self._superManager.snipsAssistantManager.clearData() def updateProjectAlice(self): self._logger.logInfo('Checking Project Alice updates') req = requests.get( url=f'{constants.GITHUB_API_URL}/ProjectAlice/branches', auth=SuperManager.getInstance().configManager.getGithubAuth()) if req.status_code != 200: self._logger.logWarning('Failed checking for updates') return userUpdatePref = SuperManager.getInstance( ).configManager.getAliceConfigByName('aliceUpdateChannel') if userUpdatePref == 'master': candidate = 'master' else: candidate = Version.fromString(constants.VERSION) for branch in req.json(): repoVersion = Version.fromString(branch['name']) if not repoVersion.isVersionNumber: continue releaseType = repoVersion.releaseType if userUpdatePref == 'rc' and releaseType in { 'b', 'a' } or userUpdatePref == 'beta' and releaseType == 'a': continue if repoVersion > candidate: candidate = repoVersion self._logger.logInfo(f'Checking on "{str(candidate)}" update channel') commons = SuperManager.getInstance().commons commons.runSystemCommand(['git', '-C', commons.rootDir(), 'stash']) commons.runSystemCommand( ['git', '-C', commons.rootDir(), 'clean', '-df']) commons.runSystemCommand( ['git', '-C', commons.rootDir(), 'checkout', str(candidate)]) commons.runSystemCommand(['git', '-C', commons.rootDir(), 'pull']) # Remove install tickets [ file.unlink() for file in Path( commons.rootDir(), 'system/skillInstallTickets').glob('*') if file.is_file() ]
class WakewordUploadThread(Thread): def __init__(self, host: str, port: int, zipPath: str): super().__init__() self._logger = Logger() self.setDaemon(True) self._host = host self._port = port self._zipPath = Path(zipPath) def run(self): try: wakewordName = self._zipPath.stem with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: sock.bind((self._host, self._port)) self._logger.logInfo('Waiting for a device to connect') sock.listen() conn, addr = sock.accept() self._logger.logInfo(f'New device connected: {addr}') with self._zipPath.open(mode='rb') as f: data = f.read(1024) while data: conn.send(data) data = f.read(1024) self._logger.logInfo(f'Waiting on a feedback from {addr[0]}') conn.settimeout(20) try: while True: answer = conn.recv(1024).decode() if not answer: self._logger.logInfo( 'The device closed the connection before confirming...' ) break if answer == '0': self._logger.logInfo( f'Wakeword "{wakewordName}" upload to {addr[0]} success' ) break elif answer == '-1': self._logger.logWarning( 'The device failed downloading the hotword') break elif answer == '-2': self._logger.logWarning( 'The device failed installing the hotword') break except timeout: self._logger.logWarning( 'The device did not confirm the operation as successfull in time. The hotword installation might have failed' ) except Exception as e: self._logger.logInfo(f'Error uploading wakeword: {e}')
class Light: state: dict swupdate: dict type: str name: str modelid: str manufacturername: str productname: str capabilities: dict config: dict uniqueid: str swversion: str swconfigid: str = '' productid: str = '' id: int = 0 bridge: Optional[Bridge] = None myScenes: list = field(default_factory=list) logger: Optional[Logger] = None def init(self, lightId: int, bridgeInstance: Bridge): self.id = lightId self.bridge = bridgeInstance self.name = self.name.lower() self.logger = Logger(prepend='[Phue Light]') def __str__(self) -> str: return f'Light id {self.id} named "{self.name}" of type {self.type}.' def on(self): #NOSONAR self.request(url=f'/{self.id}/state', method='PUT', data={'on': True}) def off(self): self.request(url=f'/{self.id}/state', method='PUT', data={'on': False}) @property def isOn(self) -> bool: return self.state['on'] @property def isOff(self) -> bool: return not self.state['on'] def alert(self, state: str = 'lselect'): self.request(url=f'/{self.id}/state', method='PUT', data={'alert': state}) def effect(self, effect: str = 'colorloop'): self.request(url=f'/{self.id}/state', method='PUT', data={'effect': effect}) def configure(self, data: dict, sendToBridge: bool = True): for key, value in data.items(): if not key in self.state: continue self.state[key] = value if sendToBridge: self.request(url=f'/{self.id}/state', method='PUT', data=data) @property def brightness(self) -> int: return self.state['bri'] @brightness.setter def brightness(self, value: int): if value == 0: self.off() self.state['bri'] = 0 return value = sorted((1, value, 254))[1] self.state['bri'] = value self.request(url=f'/{self.id}/state', method='PUT', data={'bri': value}) @property def saturation(self) -> int: return self.state['sat'] @saturation.setter def saturation(self, value: int): value = sorted((1, value, 254))[1] self.state['sat'] = value self.request(url=f'/{self.id}/state', method='PUT', data={'sat': value}) @property def hue(self) -> int: return self.state['hue'] @hue.setter def hue(self, value: int): value = sorted((0, value, 65535))[1] self.state['hue'] = value self.request(url=f'/{self.id}/state', method='PUT', data={'hue': value}) @property def xy(self) -> list: #NOSONAR return self.state['xy'] @xy.setter def xy(self, value: list): #NOSONAR x = sorted((0, value[0], 1))[1] #NOSONAR y = sorted((0, value[1], 1))[1] #NOSONAR self.state['xy'] = [x, y] self.request(url=f'/{self.id}/state', method='PUT', data={'xy': value}) @property def mired(self) -> int: return self.state['ct'] @mired.setter def mired(self, value: int): self.state['ct'] = value self.request(url=f'/{self.id}/state', method='PUT', data={'ct': value}) @property def colormode(self) -> str: return self.state.get('colormode', None) @colormode.setter def colormode(self, mode: str): if 'colormode' not in self.state: self.logger.logWarning( f'Light {self.name} with id {self.id} does not support colormode changing' ) return if mode not in ('hs', 'xy', 'ct'): mode = 'ct' self.logger.logWarning( 'Invalid color mode specified. Allowed value are "hs", "ct", "xy"' ) self.state['colormode'] = mode self.request(url=f'/{self.id}/state', method='PUT', data={'colormode': mode}) @property def reachable(self) -> bool: return self.state['reachable'] def delete(self): self.request(url=f'/{self.id}', method='DELETE') def request(self, url: str, data: dict = None, method: str = 'GET'): if not self.reachable or not self.bridge: raise LightNotReachable self.bridge.sendAuthRequest( url=f'/lights{"/" if not url.startswith("/") else ""}{url}', method=method, data=data)
def __init__(self, logDepth: int = 3, *args, **kwargs): self._depth = logDepth self._logger = Logger(logDepth)
def init(self, lightId: int, bridgeInstance: Bridge): self.id = lightId self.bridge = bridgeInstance self.name = self.name.lower() self.logger = Logger(prepend='[Phue Light]')
class ProjectAlice(Singleton): NAME = 'ProjectAlice' def __init__(self, restartHandler: callable): Singleton.__init__(self, self.NAME) self._logger = Logger(prepend='[Project Alice]') self._logger.logInfo('Starting Alice satellite unit') self._booted = False with Stopwatch() as stopWatch: self._restart = False self._restartHandler = restartHandler self._superManager = SuperManager(self) self._superManager.initManagers() self._superManager.onStart() if self._superManager.configManager.getAliceConfigByName('useHLC'): self._superManager.commons.runRootSystemCommand(['systemctl', 'start', 'hermesledcontrol']) self._superManager.onBooted() self._logger.logInfo(f'Started in {stopWatch} seconds') self._booted = True @property def name(self) -> str: return self.NAME @property def isBooted(self) -> bool: return self._booted @property def restart(self) -> bool: return self._restart @restart.setter def restart(self, value: bool): self._restart = value def doRestart(self): self._restart = True self.onStop() def onStop(self): self._logger.logInfo('Shutting down') self._superManager.onStop() if self._superManager.configManager.getAliceConfigByName('useHLC'): self._superManager.commons.runRootSystemCommand(['systemctl', 'stop', 'hermesledcontrol']) self.INSTANCE = None self._restartHandler() def updateProjectAlice(self): self._logger.logInfo('Checking for satellite updates') req = requests.get(url=f'{constants.GITHUB_API_URL}/ProjectAliceSatellite/branches', auth=SuperManager.getInstance().configManager.getGithubAuth()) if req.status_code != 200: self._logger.logWarning('Failed checking for updates') return userUpdatePref = SuperManager.getInstance().configManager.getAliceConfigByName('aliceUpdateChannel') if userUpdatePref == 'master': candidate = 'master' else: candidate = Version.fromString(constants.VERSION) for branch in req.json(): repoVersion = Version.fromString(branch['name']) if not repoVersion.isVersionNumber: continue releaseType = repoVersion.releaseType if userUpdatePref == 'rc' and releaseType in {'b', 'a'} or userUpdatePref == 'beta' and releaseType == 'a': continue if repoVersion > candidate: candidate = repoVersion self._logger.logInfo(f'Checking on "{str(candidate)}" update channel') commons = SuperManager.getInstance().commons commons.runSystemCommand(['git', '-C', commons.rootDir(), 'stash']) commons.runSystemCommand(['git', '-C', commons.rootDir(), 'clean', '-df']) commons.runSystemCommand(['git', '-C', commons.rootDir(), 'checkout', str(candidate)]) commons.runSystemCommand(['git', '-C', commons.rootDir(), 'pull'])