class WakewordUploadThread(Thread):
    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)

    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 with address **{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:
                            raise Exception(
                                'The device closed the connection before confirming...'
                            )
                        if answer == '0':
                            self._logger.logInfo(
                                f'Wakeword **{wakewordName}** upload to **{addr[0]}** success'
                            )
                            break
                        elif answer == '-1':
                            raise Exception(
                                'The device failed downloading the hotword')
                        elif answer == '-2':
                            raise Exception(
                                'The device failed installing the hotword')
                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.logError(f'Error uploading wakeword: {e}')
Ejemplo n.º 2
0
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)
Ejemplo n.º 3
0
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
Ejemplo n.º 4
0
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()
        ]
Ejemplo n.º 5
0
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'])