示例#1
0
    def __init__(self, params):
        self._logger = logging.getLogger('HermesLedControl')
        self._logger.info('Initializing HermesLedControl')

        self._mqttClient = None
        self._hardwareReference = None
        self._ledsController = None
        self._params = params

        self._mqttServer = 'localhost'
        self._me = 'default'
        self._mqttPort = 1883
        self._mqttUsername = ''
        self._mqttPassword = ''
        self._tlsFile = ''

        self._hotwordRegex = re.compile(
            self._SUB_ON_HOTWORD.replace('+', '(.*)'))

        if params.engine == 'projectalice':
            from models.engines.ProjectAlice import ProjectAlice
            engine = ProjectAlice()
        elif params.engine == 'rhasspy':
            from models.engines.Rhasspy import Rhasspy
            engine = Rhasspy()
        elif params.engine == 'snips':
            from models.engines.Snips import Snips
            engine = Snips()
        else:
            self._logger.error('Unsupported assistant engine "{}"'.format(
                params.engine))
            self.onStop()
            return

        self._configs = engine.loadConfig(params)
        if not self._configs:
            self.onStop()

        with open('hardware.json') as f:
            self._hardwareReference = json.load(f)
            self._logger.info('Loaded {} hardware references'.format(
                len(self._hardwareReference)))

        if params.hardware not in self._hardwareReference:
            self._logger.fatal('Trying to use an unsupported hardware')
            self.onStop()
        else:
            self._hardware = self._hardwareReference[self._params.hardware]

        self._mqttServer = params.mqttServer or self._configs['mqttServer']
        self._mqttPort = int(params.mqttPort or self._configs['mqttPort'])
        self._mqttUsername = params.mqttUsername or self._configs[
            'mqttUsername']
        self._mqttPassword = params.mqttPassword or self._configs[
            'mqttPassword']
        self._tlsFile = self._configs['mqttTLSCAFile']
        self._me = params.clientId or self._configs['deviceName']

        self._SUB_ON_PLAY_FINISHED = self._SUB_ON_PLAY_FINISHED.format(
            self._me)

        self._logger.info('- Mqtt server set to {}'.format(self._mqttServer))
        self._logger.info('- Mqtt port set to {}'.format(self._mqttPort))

        if self._mqttUsername:
            self._logger.info('- Mqtt username set to {}'.format(
                self._mqttUsername))
        if self._mqttPassword:
            self._logger.info('- Mqtt password set to "hidden"')

        self._logger.info('- Client id set to {}'.format(self._me))
        self._logger.info('- Hardware set to {}'.format(
            self._hardware['name']))

        if params.leds is not None:
            self._hardware['numberOfLeds'] = params.leds
        self._logger.info('- Using {} as pattern with {} leds'.format(
            params.pattern, self._hardware['numberOfLeds']))

        if 'gpioPin' in self._hardware:
            if params.gpioPin is not None:
                self._hardware['gpioPin'] = params.gpioPin
            self._logger.info('Using pin #{}'.format(
                self._hardware['gpioPin']))

        if 'vid' in self._hardware and params.vid is not None:
            self._hardware['vid'] = params.vid

        if 'gpios' in self._hardware and len(params.pureGpioPinout) > 0:
            self.hardware['gpios'] = params.pureGpioPinout

        if 'activeHigh' in self._hardware:
            self._hardware['activeHigh'] = params.activeHigh

        if 'endFrame' in self._hardware and params.endFrame is not None:
            self._hardware['endFrame'] = params.endFrame

        self._ledsController = LedsController(self)
        self._mqttClient = self.connectMqtt()
示例#2
0
class HermesLedControl:

    ALL_SITE_ID = 'all'

    _SUB_ON_HOTWORD = 'hermes/hotword/+/detected'
    _SUB_ON_SAY = 'hermes/tts/say'
    _SUB_ON_THINK = 'hermes/asr/textCaptured'
    _SUB_ON_LISTENING = 'hermes/asr/startListening'
    _SUB_ON_HOTWORD_TOGGLE_ON = 'hermes/hotword/toggleOn'
    _SUB_LEDS_ON_ERROR = 'hermes/nlu/intentNotRecognized'
    _SUB_LEDS_ON_SUCCESS = 'hermes/nlu/intentParsed'
    _SUB_ON_PLAY_FINISHED = 'hermes/audioServer/{}/playFinished'
    _SUB_ON_TTS_FINISHED = 'hermes/tts/sayFinished'

    _SUB_ON_LEDS_TOGGLE = 'hermes/leds/toggle'
    _SUB_ON_LEDS_TOGGLE_ON = 'hermes/leds/toggleOn'
    _SUB_ON_LEDS_TOGGLE_OFF = 'hermes/leds/toggleOff'
    _SUB_ON_LEDS_CLEAR = 'hermes/leds/clear'
    _SUB_ON_LEDS_IDLE = 'hermes/leds/idle'
    _SUB_UPDATING = 'hermes/leds/systemUpdate'
    _SUB_ON_CALL = 'hermes/leds/onCall'
    _SUB_SETUP_MODE = 'hermes/leds/setupMode'
    _SUB_CON_ERROR = 'hermes/leds/connectionError'
    _SUB_ON_MESSAGE = 'hermes/leds/onMessage'
    _SUB_ON_DND = 'hermes/leds/doNotDisturb'
    _SUB_ON_START = 'hermes/leds/onStart'
    _SUB_ON_STOP = 'hermes/leds/onStop'

    _SUB_VOLUME_SET = 'hermes/volume/set'
    _SUB_VADLED_SET = 'hermes/leds/vadLed'
    _SUB_MANUAL_ANIMATIONS_SET = 'hermes/leds/manual/animations'

    def __init__(self, params):
        self._logger = logging.getLogger('HermesLedControl')
        self._logger.info('Initializing HermesLedControl')

        self._mqttClient = None
        self._hardwareReference = None
        self._ledsController = None
        self._params = params

        self._mqttServer = 'localhost'
        self._me = 'default'
        self._mqttPort = 1883
        self._mqttUsername = ''
        self._mqttPassword = ''
        self._tlsFile = ''

        self._hotwordRegex = re.compile(
            self._SUB_ON_HOTWORD.replace('+', '(.*)'))

        if params.engine == 'projectalice':
            from models.engines.ProjectAlice import ProjectAlice
            engine = ProjectAlice()
        elif params.engine == 'rhasspy':
            from models.engines.Rhasspy import Rhasspy
            engine = Rhasspy()
        elif params.engine == 'snips':
            from models.engines.Snips import Snips
            engine = Snips()
        else:
            self._logger.error('Unsupported assistant engine "{}"'.format(
                params.engine))
            self.onStop()
            return

        self._configs = engine.loadConfig(params)
        if not self._configs:
            self.onStop()

        with open('hardware.json') as f:
            self._hardwareReference = json.load(f)
            self._logger.info('Loaded {} hardware references'.format(
                len(self._hardwareReference)))

        if params.hardware not in self._hardwareReference:
            self._logger.fatal('Trying to use an unsupported hardware')
            self.onStop()
        else:
            self._hardware = self._hardwareReference[self._params.hardware]

        self._mqttServer = params.mqttServer or self._configs['mqttServer']
        self._mqttPort = int(params.mqttPort or self._configs['mqttPort'])
        self._mqttUsername = params.mqttUsername or self._configs[
            'mqttUsername']
        self._mqttPassword = params.mqttPassword or self._configs[
            'mqttPassword']
        self._tlsFile = self._configs['mqttTLSCAFile']
        self._me = params.clientId or self._configs['deviceName']

        self._SUB_ON_PLAY_FINISHED = self._SUB_ON_PLAY_FINISHED.format(
            self._me)

        self._logger.info('- Mqtt server set to {}'.format(self._mqttServer))
        self._logger.info('- Mqtt port set to {}'.format(self._mqttPort))

        if self._mqttUsername:
            self._logger.info('- Mqtt username set to {}'.format(
                self._mqttUsername))
        if self._mqttPassword:
            self._logger.info('- Mqtt password set to "hidden"')

        self._logger.info('- Client id set to {}'.format(self._me))
        self._logger.info('- Hardware set to {}'.format(
            self._hardware['name']))

        if params.leds is not None:
            self._hardware['numberOfLeds'] = params.leds
        self._logger.info('- Using {} as pattern with {} leds'.format(
            params.pattern, self._hardware['numberOfLeds']))

        if 'gpioPin' in self._hardware:
            if params.gpioPin is not None:
                self._hardware['gpioPin'] = params.gpioPin
            self._logger.info('Using pin #{}'.format(
                self._hardware['gpioPin']))

        if 'vid' in self._hardware and params.vid is not None:
            self._hardware['vid'] = params.vid

        if 'gpios' in self._hardware and len(params.pureGpioPinout) > 0:
            self.hardware['gpios'] = params.pureGpioPinout

        if 'activeHigh' in self._hardware:
            self._hardware['activeHigh'] = params.activeHigh

        if 'endFrame' in self._hardware and params.endFrame is not None:
            self._hardware['endFrame'] = params.endFrame

        self._ledsController = LedsController(self)
        self._mqttClient = self.connectMqtt()

    def onStart(self):
        self._ledsController.onStart()
        self._logger.info('Hermes Led Control started')

    def onStop(self):
        if self._mqttClient is not None:
            self._mqttClient.disconnect()

        if self._ledsController is not None:
            self._ledsController.onStop()

        sys.exit(0)

    def connectMqtt(self):
        try:
            mqttClient = mqtt.Client()

            if self._mqttUsername and self._mqttPassword:
                mqttClient.username_pw_set(self._mqttUsername,
                                           self._mqttPassword)

            mqttClient.on_log = self.onLog
            mqttClient.on_connect = self.onConnect
            mqttClient.on_message = self.onMessage

            if self._tlsFile:
                mqttClient.tls_set(certfile=self._tlsFile)
                mqttClient.tls_insecure_set(False)

            mqttClient.connect(self._mqttServer, int(self._mqttPort))
            mqttClient.loop_start()
            return mqttClient
        except:
            self._logger.fatal("Couldn't connect to mqtt, aborting")
            self.onStop()

    # noinspection PyUnusedLocal
    def onLog(self, client, userdata, level, buf):
        if level != 16:
            self._logger.error(buf)

    # noinspection PyUnusedLocal
    def onConnect(self, client, userdata, flags, rc):
        time.sleep(0.1)
        self._mqttClient.subscribe([
            (self._SUB_ON_HOTWORD, 0),
            (self._SUB_ON_SAY, 0),
            (self._SUB_ON_THINK, 0),
            (self._SUB_ON_LISTENING, 0),
            (self._SUB_ON_PLAY_FINISHED, 0),
            (self._SUB_ON_TTS_FINISHED, 0),
            (self._SUB_ON_LEDS_TOGGLE_ON, 0),
            (self._SUB_ON_LEDS_TOGGLE_OFF, 0),
            (self._SUB_ON_LEDS_TOGGLE, 0),
            (self._SUB_LEDS_ON_ERROR, 0),
            (self._SUB_LEDS_ON_SUCCESS, 0),
            (self._SUB_ON_START, 0),
            (self._SUB_ON_STOP, 0),
            (self._SUB_UPDATING, 0),
            (self._SUB_ON_CALL, 0),
            (self._SUB_SETUP_MODE, 0),
            (self._SUB_CON_ERROR, 0),
            (self._SUB_ON_MESSAGE, 0),
            (self._SUB_ON_DND, 0),
            (self._SUB_VOLUME_SET, 0),
            (self._SUB_VADLED_SET, 0),
            (self._SUB_ON_LEDS_CLEAR, 0),
            (self._SUB_ON_LEDS_IDLE, 0),
            (self._SUB_MANUAL_ANIMATIONS_SET, 0),
        ])

        self._mqttClient.subscribe(self._params.offListener)

    # noinspection PyUnusedLocal
    def onMessage(self, client, userdata, message: MQTTMessage):
        payload = dict()

        if hasattr(message, 'payload') and message.payload:
            payload = json.loads(message.payload.decode('UTF-8'))

        noLeds = payload.get('noLeds', False)

        if noLeds:
            return False

        siteId = payload.get('siteId')
        sticky = payload.get('sticky', False)
        isForMe = siteId == self._me or siteId == self.ALL_SITE_ID

        if self._hotwordRegex.match(message.topic):
            if isForMe:
                if self._params.debug:
                    self._logger.debug('On hotword triggered')
                self._ledsController.wakeup(sticky)
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On hotword received but it wasn't for me")

        elif message.topic == self._SUB_ON_LISTENING:
            if isForMe:
                if self._params.debug:
                    self._logger.debug('On listen triggered')
                self._ledsController.listen(sticky)
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On listen received but it wasn't for me")

        elif message.topic == self._SUB_ON_SAY:
            if isForMe:
                if self._params.debug:
                    self._logger.debug('On say triggered')
                self._ledsController.speak(sticky)
            else:
                if self._params.debug:
                    self._logger.debug("On say received but it wasn't for me")

        elif message.topic == self._SUB_ON_THINK:
            if isForMe:
                if self._params.debug:
                    self._logger.debug('On think triggered')
                self._ledsController.think(sticky)
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On think received but it wasn't for me")

        elif message.topic == self._SUB_ON_HOTWORD_TOGGLE_ON:
            if isForMe:
                if self._params.debug:
                    self._logger.debug('On hotword toggle on triggered')
                self._ledsController.idle()
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On hotword toggle on received but it wasn't for me")

        elif message.topic == self._SUB_ON_TTS_FINISHED:
            if isForMe:
                if self._params.debug:
                    self._logger.debug('On tts finished triggered')
                self._ledsController.idle()
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On tts finished received but it wasn't for me")

        elif message.topic == self._SUB_ON_PLAY_FINISHED:
            if isForMe:
                if self._params.debug:
                    self._logger.debug('On play finished triggered')
                self._ledsController.idle()
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On play finished received but it wasn't for me")

        elif message.topic == self._SUB_ON_LEDS_TOGGLE_ON:
            if isForMe:
                if self._params.debug:
                    self._logger.debug('On leds toggle on triggered')
                self._ledsController.toggleStateOn()
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On leds toggle on received but it wasn't for me")

        elif message.topic == self._SUB_ON_LEDS_TOGGLE_OFF:
            if isForMe:
                if self._params.debug:
                    self._logger.debug('On leds toggle off triggered')
                self._ledsController.toggleStateOff()
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On leds toggle off received but it wasn't for me")

        elif message.topic == self._SUB_ON_LEDS_TOGGLE:
            if isForMe:
                if self._params.debug:
                    self._logger.debug('On leds toggle triggered')
                self._ledsController.toggleState()
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On leds toggle received but it wasn't for me")

        elif message.topic == self._SUB_LEDS_ON_SUCCESS:
            if isForMe:
                if self._params.debug:
                    self._logger.debug('On success triggered')
                self._ledsController.onSuccess(sticky)
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On success received but it wasn't for me")

        elif message.topic == self._SUB_LEDS_ON_ERROR:
            if isForMe:
                if self._params.debug:
                    self._logger.debug('On error triggered')
                self._ledsController.onError(sticky)
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On error received but it wasn't for me")

        elif message.topic == self._SUB_UPDATING:
            if isForMe:
                if self._params.debug:
                    self._logger.debug('On updating triggered')
                self._ledsController.updating(sticky)
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On updating received but it wasn't for me")

        elif message.topic == self._SUB_ON_CALL:
            if isForMe:
                if self._params.debug:
                    self._logger.debug('On call triggered')
                self._ledsController.call(sticky)
            else:
                if self._params.debug:
                    self._logger.debug("On call received but it wasn't for me")

        elif message.topic == self._SUB_SETUP_MODE:
            if isForMe:
                if self._params.debug:
                    self._logger.debug('On setup mode triggered')
                self._ledsController.setupMode(sticky)
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On setup mode received but it wasn't for me")

        elif message.topic == self._SUB_CON_ERROR:
            if isForMe:
                if self._params.debug:
                    self._logger.debug('On connection error triggered')
                self._ledsController.conError(sticky)
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On connection error received but it wasn't for me")

        elif message.topic == self._SUB_ON_MESSAGE:
            if isForMe:
                if self._params.debug:
                    self._logger.debug('On message triggered')
                self._ledsController.message(sticky)
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On message received but it wasn't for me")

        elif message.topic == self._SUB_ON_DND:
            if isForMe:
                if self._params.debug:
                    self._logger.debug('On do not disturb triggered')
                self._ledsController.dnd(sticky)
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On do not disturb received but it wasn't for me")

        elif message.topic == self._SUB_ON_START:
            if isForMe:
                if self._params.debug:
                    self._logger.debug('On start triggered')
                self._ledsController.start()
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On start received but it wasn't for me")

        elif message.topic == self._SUB_ON_STOP:
            if isForMe:
                if self._params.debug:
                    self._logger.debug('On stop triggered')
                self._ledsController.stop()
            else:
                if self._params.debug:
                    self._logger.debug("On stop received but it wasn't for me")

        elif message.topic == self._SUB_VOLUME_SET:
            if isForMe:
                if self._params.debug:
                    self._logger.debug('On volume set triggered')
                if 'volume' not in payload:
                    self._logger.error(
                        'Missing "volume" in payload for set volume')
                else:
                    self._ledsController.setVolume(payload['volume'])
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On volume set received but it wasn't for me")

        elif message.topic == self._SUB_VADLED_SET:
            if isForMe:
                if self._params.debug:
                    self._logger.debug('On vad led set triggered')
                if 'state' not in payload:
                    self._logger.error(
                        'Missing "state" in payload for set vad led')
                else:
                    self._ledsController.setVadLed(payload['state'])
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On vad led set received but it wasn't for me")

        elif message.topic == self._SUB_ON_LEDS_IDLE:
            if isForMe:
                if self._params.debug:
                    self._logger.debug('On leds idle triggered')
                self._ledsController.idle()
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On leds idle received but it wasn't for me")

        elif message.topic == self._SUB_ON_LEDS_CLEAR:
            self._ledsController.stickyAnimation = None
            if isForMe:
                if self._params.debug:
                    self._logger.debug('On leds clear triggered')
                self._ledsController.clearLeds()
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On leds clear received but it wasn't for me")

        elif message.topic == self._SUB_MANUAL_ANIMATIONS_SET:
            if isForMe:
                if self._params.debug:
                    self._logger.debug(
                        'On manual animation leds set triggered')

                if 'animation' not in payload:
                    self._logger.error(
                        'Missing "animation" in payload for set manual animation leds'
                    )
                else:
                    flush = 'flush' in payload and payload['flush']
                    clear = 'clear' in payload and payload['clear']
                    duration = self.safePayloadNumber(payload, 'duration', 0)

                    if clear:
                        self._ledsController.stickyAnimation = None

                    if payload['animation'] == 'breath':
                        self._ledsController.putStickyPattern(
                            pattern=self._ledsController.pattern.animator.
                            breath,
                            sticky=sticky,
                            duration=duration,
                            flush=flush,
                            color=self.safePayloadColor(payload, 'color'),
                            minBrightness=self.safePayloadNumber(
                                payload, 'minBrightness', 2),
                            maxBrightness=self.safePayloadNumber(
                                payload, 'maxBrightness', 20),
                            speed=self.safePayloadNumber(payload, 'speed', 40))
                    elif payload['animation'] == 'blink':
                        self._ledsController.putStickyPattern(
                            pattern=self._ledsController.pattern.animator.
                            blink,
                            sticky=sticky,
                            duration=duration,
                            flush=flush,
                            color=self.safePayloadColor(payload, 'color'),
                            minBrightness=self.safePayloadNumber(
                                payload, 'minBrightness', 2),
                            maxBrightness=self.safePayloadNumber(
                                payload, 'maxBrightness', 20),
                            speed=self.safePayloadNumber(
                                payload, 'speed', 300),
                            repeat=self.safePayloadNumber(
                                payload, 'repeat', 3),
                            smooth=payload.get('smooth', True))
                    elif payload['animation'] == 'rotate':
                        self._ledsController.putStickyPattern(
                            pattern=self._ledsController.pattern.animator.
                            rotate,
                            sticky=sticky,
                            duration=duration,
                            flush=flush,
                            color=self.safePayloadColor(payload, 'color'),
                            speed=self.safePayloadNumber(payload, 'speed', 20),
                            trail=self.safePayloadNumber(payload, 'trail', 1),
                            startAt=self.safePayloadNumber(
                                payload, 'startAt', 0))
                    elif payload['animation'] == 'doubleSidedFilling':
                        self._ledsController.putStickyPattern(
                            pattern=self._ledsController.pattern.animator.
                            doubleSidedFilling,
                            sticky=sticky,
                            duration=duration,
                            flush=flush,
                            color=self.safePayloadColor(payload, 'color'),
                            startAt=self.safePayloadNumber(
                                payload, 'startAt', 0),
                            direction=self.safePayloadNumber(
                                payload, 'direction', 1),
                            speed=self.safePayloadNumber(payload, 'speed', 50),
                            new=payload.get('new', True))
                    elif payload['animation'] == 'doublePingPong':
                        self._ledsController.putStickyPattern(
                            pattern=self._ledsController.pattern.animator.
                            doublePingPong,
                            sticky=sticky,
                            duration=duration,
                            flush=flush,
                            color=self.safePayloadColor(payload, 'color'),
                            speed=self.safePayloadNumber(payload, 'speed', 20),
                            backgroundColor=self.safePayloadColor(
                                payload, 'backgroundColor'),
                            startAt=self.safePayloadNumber(
                                payload, 'startAt', 0))
                    elif payload['animation'] == 'waitWheel':
                        self._ledsController.putStickyPattern(
                            pattern=self._ledsController.pattern.animator.
                            waitWheel,
                            sticky=sticky,
                            duration=duration,
                            flush=flush,
                            color=self.safePayloadColor(payload, 'color'),
                            speed=self.safePayloadNumber(payload, 'speed', 20),
                            backgroundColor=self.safePayloadColor(
                                payload, 'backgroundColor'),
                            startAt=self.safePayloadNumber(
                                payload, 'startAt', 0))
                    elif payload['animation'] == 'relayRace':
                        self._ledsController.putStickyPattern(
                            pattern=self._ledsController.pattern.animator.
                            relayRace,
                            sticky=sticky,
                            duration=duration,
                            flush=flush,
                            color=self.safePayloadColor(payload, 'color'),
                            relayColor=self.safePayloadColor(
                                payload, 'relayColor'),
                            backgroundColor=self.safePayloadColor(
                                payload, 'backgroundColor'),
                            speed=self.safePayloadNumber(payload, 'speed', 20),
                            startAt=self.safePayloadNumber(
                                payload, 'startAt', 0))
                    elif payload['animation'] == 'rainbow':
                        self._ledsController.putStickyPattern(
                            pattern=self._ledsController.pattern.animator.
                            rainbow,
                            sticky=sticky,
                            duration=duration,
                            flush=flush,
                            brightness=self.safePayloadNumber(
                                payload, 'brightness', 255),
                            speed=self.safePayloadNumber(
                                payload, 'speed', 100))
                    elif payload['animation'] == 'wheelOverlap':
                        self._ledsController.putStickyPattern(
                            pattern=self._ledsController.pattern.animator.
                            wheelOverlap,
                            sticky=sticky,
                            duration=duration,
                            flush=flush,
                            colors=payload['colors'],
                            brightness=self.safePayloadNumber(
                                payload, 'brightness', 255),
                            speed=self.safePayloadNumber(
                                payload, 'speed', 100))
                    elif payload['animation'] == 'windmill':
                        self._ledsController.putStickyPattern(
                            pattern=self._ledsController.pattern.animator.
                            windmill,
                            sticky=sticky,
                            duration=duration,
                            flush=flush,
                            colors=payload['colors'],
                            smooth=payload.get('smooth', True),
                            trail=self.safePayloadNumber(payload, 'trail', 0),
                            trailAttenuation=self.safePayloadNumber(
                                payload, 'trailAttenuation', 0, True),
                            speed=self.safePayloadNumber(payload, 'speed', 20))
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On manual animation leds received but it wasn't for me"
                    )

    def safePayloadColor(self, payload, attributeName, default=None):
        color = payload.get(attributeName,
                            [255, 255, 255, 2] if not default else default)

        if type(color) == str and ',' in color:
            color = [int(c) for c in color.split(",")]

        if len(color) == 3:
            self._logger.warning(
                f"Missing white channel in '{attributeName}' attribute (RGBW format), appending default value"
            )
            color = color + [255]
        elif len(color) != 4:
            self._logger.error(
                f"Bad color '{color}' for '{attributeName}' attribute (RGBW format), switching to default: '{default}'"
            )
            color = default

        return color

    def safePayloadNumber(self,
                          payload,
                          attributeName,
                          default=None,
                          float=False):
        number = payload.get(attributeName, default)

        try:
            if float:
                number = float(number)
            else:
                number = int(number)
        except:
            self._logger.error(
                f"Bad value '{number}' for '{attributeName}' attribute (number format), switching to default: '{default}'"
            )
            number = default

        return number

    @property
    def params(self):
        return self._params

    @property
    def hardwareReference(self) -> dict:
        return self._hardwareReference

    @property
    def hardware(self) -> dict:
        return self._hardware
class SnipsLedControl:

    _SUB_ON_HOTWORD = 'hermes/hotword/+/detected'
    _SUB_ON_SAY = 'hermes/tts/say'
    _SUB_ON_THINK = 'hermes/asr/textCaptured'
    _SUB_ON_LISTENING = 'hermes/asr/startListening'
    _SUB_ON_HOTWORD_TOGGLE_ON = 'hermes/hotword/toggleOn'
    _SUB_LEDS_ON_ERROR = 'hermes/nlu/intentNotRecognized'
    _SUB_LEDS_ON_SUCCESS = 'hermes/nlu/intentParsed'
    _SUB_ON_PLAY_FINISHED = 'hermes/audioServer/{}/playFinished'
    _SUB_ON_TTS_FINISHED = 'hermes/tts/sayFinished'

    _SUB_ON_LEDS_TOGGLE = 'hermes/leds/toggle'
    _SUB_ON_LEDS_TOGGLE_ON = 'hermes/leds/toggleOn'
    _SUB_ON_LEDS_TOGGLE_OFF = 'hermes/leds/toggleOff'
    _SUB_ON_LEDS_CLEAR = 'hermes/leds/clear'
    _SUB_UPDATING = 'hermes/leds/systemUpdate'
    _SUB_ON_CALL = 'hermes/leds/onCall'
    _SUB_SETUP_MODE = 'hermes/leds/setupMode'
    _SUB_CON_ERROR = 'hermes/leds/connectionError'
    _SUB_ON_MESSAGE = 'hermes/leds/onMessage'
    _SUB_ON_DND = 'hermes/leds/doNotDisturb'
    _SUB_ON_START = 'hermes/leds/onStart'
    _SUB_ON_STOP = 'hermes/leds/onStop'

    _SUB_VOLUME_SET = 'hermes/volume/set'
    _SUB_VADLED_SET = 'hermes/leds/vadLed'

    def __init__(self, params):
        self._logger = logging.getLogger('SnipsLedControl')
        self._logger.info('Initializing SnipsLedControl')

        self._mqttClient = None
        self._hardwareReference = None
        self._ledsController = None
        self._params = params

        self._snipsConfigs = self.loadConfigs()

        self._mqttServer = 'localhost'
        self._me = 'default'
        self._mqttPort = 1883
        self._mqttUsername = ''
        self._mqttPassword = ''
        self._tlsFile = ''

        self._hotwordRegex = re.compile(
            self._SUB_ON_HOTWORD.replace('+', '(.*)'))

        with open('hardware.json') as f:
            self._hardwareReference = json.load(f)
            self._logger.info('Loaded {} hardware references'.format(
                len(self._hardwareReference)))

        if params.hardware not in self._hardwareReference:
            self._logger.fatal('Trying to use an unsupported hardware')
            self.onStop()
        else:
            self._hardware = self._hardwareReference[self._params.hardware]

        if params.mqttServer is None:
            try:
                if 'snips-common' in self._snipsConfigs and 'mqtt' in self._snipsConfigs[
                        'snips-common']:
                    self._mqttServer = self._snipsConfigs['snips-common'][
                        'mqtt'].replace(':1883', '')

                    if 'mqtt_username' in self._snipsConfigs[
                            'snips-common'] and params.mqttUsername is None:
                        self._mqttUsername = self._snipsConfigs[
                            'snips-common']['mqtt_username']

                    if 'mqtt_password' in self._snipsConfigs[
                            'snips-common'] and params.mqttPassword is None:
                        self._mqttPassword = self._snipsConfigs[
                            'snips-common']['mqtt_password']

                    if 'mqtt_tls_cafile' in self._snipsConfigs['snips-common']:
                        self._tlsFile = self._snipsConfigs['snips-common'][
                            'mqtt_tls_cafile']

            except:
                self._logger.info(
                    '- Falling back to default config for mqtt server')
        else:
            self._mqttServer = params.mqttServer

        if params.clientId is None:
            try:
                if 'snips-audio-server' in self._snipsConfigs and 'bind' in self._snipsConfigs[
                        'snips-audio-server']:
                    self._me = self._snipsConfigs['snips-audio-server'][
                        'bind'].replace('@mqtt', '')
            except:
                self._logger.info(
                    '- Falling back to default config for client id')
        else:
            self._me = params.clientId

        self._SUB_ON_PLAY_FINISHED = self._SUB_ON_PLAY_FINISHED.format(
            self._me)

        if params.mqttPort is None:
            try:
                if 'snips-common' in self._snipsConfigs and 'mqtt' in self._snipsConfigs[
                        'snips-common']:
                    self._mqttPort = self._snipsConfigs['snips-common'][
                        'mqtt'].split(':')[1]
            except:
                self._logger.info(
                    '- Falling back to default config for mqtt port')
        else:
            self._mqttPort = params.mqttPort

        self._logger.info('- Mqtt server set to {}'.format(self._mqttServer))
        self._logger.info('- Mqtt port set to {}'.format(self._mqttPort))

        if self._mqttUsername:
            self._logger.info('- Mqtt username set to {}'.format(
                self._mqttUsername))
        if self._mqttPassword:
            self._logger.info('- Mqtt password set to "hidden"')

        self._logger.info('- Client id set to {}'.format(self._me))
        self._logger.info('- Hardware set to {}'.format(
            self._hardware['name']))

        string = '- Using {} as pattern with {} leds'
        if params.leds is not None:
            self._logger.info(string.format(params.pattern, params.leds))
            self._hardware['numberOfLeds'] = params.leds
        else:
            self._logger.info(
                string.format(params.pattern, self._hardware['numberOfLeds']))

        if 'gpioPin' in self._hardware:
            string = 'Using pin #{}'
            if params.gpioPin is not None:
                self._logger.info(string.format(params.gpioPin))
                self._hardware['gpioPin'] = params.gpioPin
            else:
                self._logger.info(string.format(self._hardware['gpioPin']))

        if 'vid' in self._hardware and params.vid is not None:
            self._hardware['vid'] = params.vid

        if 'gpios' in self._hardware and len(params.pureGpioPinout) > 0:
            self.hardware['gpios'] = params.pureGpioPinout

        if 'activeHigh' in self._hardware and params.activeHigh != self._hardware[
                'activeHigh']:
            self._hardware['activeHigh'] = params.activeHigh

        if 'endFrame' in self._hardware and params.endFrame is not None:
            self._hardware['endFrame'] = params.endFrame

        self._ledsController = LedsController(self)
        self._mqttClient = self.connectMqtt()

    def onStart(self):
        self._ledsController.onStart()
        self._logger.info('Snips Led Control started')

    def onStop(self):
        if self._mqttClient is not None:
            self._mqttClient.disconnect()

        if self._ledsController is not None:
            self._ledsController.onStop()

        sys.exit(0)

    def loadConfigs(self):
        self._logger.info('Loading configurations')

        if os.path.isfile('/etc/snips.toml'):
            with open('/etc/snips.toml') as confFile:
                configs = toml.load(confFile)
                return configs
        else:
            if self.params.debug:
                self._logger.info(
                    'No snips config found but debug mode, allow to continue')
                return None
            else:
                self._logger.fatal('Error loading configurations')
                self.onStop()
                return None

    def connectMqtt(self):
        try:
            mqttClient = mqtt.Client()

            if self._mqttUsername and self._mqttPassword:
                mqttClient.username_pw_set(self._mqttUsername,
                                           self._mqttPassword)

            mqttClient.on_log = self.onLog
            mqttClient.on_connect = self.onConnect
            mqttClient.on_message = self.onMessage

            if self._tlsFile:
                mqttClient.tls_set(certfile=self._tlsFile)
                mqttClient.tls_insecure_set(False)

            mqttClient.connect(self._mqttServer, int(self._mqttPort))
            mqttClient.loop_start()
            return mqttClient
        except:
            self._logger.fatal("Couldn't connect to mqtt, aborting")
            self.onStop()

    # noinspection PyUnusedLocal
    def onLog(self, client, userdata, level, buf):
        if level != 16:
            self._logger.error(buf)

    # noinspection PyUnusedLocal
    def onConnect(self, client, userdata, flags, rc):
        time.sleep(0.1)
        self._mqttClient.subscribe([(self._SUB_ON_HOTWORD, 0),
                                    (self._SUB_ON_SAY, 0),
                                    (self._SUB_ON_THINK, 0),
                                    (self._SUB_ON_LISTENING, 0),
                                    (self._SUB_ON_PLAY_FINISHED, 0),
                                    (self._SUB_ON_TTS_FINISHED, 0),
                                    (self._SUB_ON_LEDS_TOGGLE_ON, 0),
                                    (self._SUB_ON_LEDS_TOGGLE_OFF, 0),
                                    (self._SUB_ON_LEDS_TOGGLE, 0),
                                    (self._SUB_LEDS_ON_ERROR, 0),
                                    (self._SUB_LEDS_ON_SUCCESS, 0),
                                    (self._SUB_ON_START, 0),
                                    (self._SUB_ON_STOP, 0),
                                    (self._SUB_UPDATING, 0),
                                    (self._SUB_ON_CALL, 0),
                                    (self._SUB_SETUP_MODE, 0),
                                    (self._SUB_CON_ERROR, 0),
                                    (self._SUB_ON_MESSAGE, 0),
                                    (self._SUB_ON_DND, 0),
                                    (self._SUB_VOLUME_SET, 0),
                                    (self._SUB_VADLED_SET, 0),
                                    (self._SUB_ON_LEDS_CLEAR, 0)])

        self._mqttClient.subscribe(self._params.offListener)

    # noinspection PyUnusedLocal
    def onMessage(self, client, userdata, message: MQTTMessage):
        payload = None

        if hasattr(message, 'payload') and message.payload:
            payload = json.loads(message.payload.decode('UTF-8'))

        siteId = None
        sticky = False
        if payload is not None:
            if 'siteId' in payload:
                siteId = payload['siteId']

            if 'sticky' in payload:
                sticky = True

        if self._hotwordRegex.match(message.topic):
            if siteId == self._me:
                if self._params.debug:
                    self._logger.debug('On hotword triggered')
                self._ledsController.wakeup(sticky)
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On hotword received but it wasn't for me")

        elif message.topic == self._SUB_ON_LISTENING:
            if siteId == self._me:
                if self._params.debug:
                    self._logger.debug('On listen triggered')
                self._ledsController.listen(sticky)
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On listen received but it wasn't for me")

        elif message.topic == self._SUB_ON_SAY:
            if siteId == self._me:
                if self._params.debug:
                    self._logger.debug('On say triggered')
                self._ledsController.speak(sticky)
            else:
                if self._params.debug:
                    self._logger.debug("On say received but it wasn't for me")

        elif message.topic == self._SUB_ON_THINK:
            if siteId == self._me:
                if self._params.debug:
                    self._logger.debug('On think triggered')
                self._ledsController.think(sticky)
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On think received but it wasn't for me")

        elif message.topic == self._SUB_ON_HOTWORD_TOGGLE_ON:
            if siteId == self._me:
                if self._params.debug:
                    self._logger.debug('On hotword toggle on triggered')
                self._ledsController.idle()
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On hotword toggle on received but it wasn't for me")

        elif message.topic == self._SUB_ON_TTS_FINISHED:
            if siteId == self._me:
                if self._params.debug:
                    self._logger.debug('On tts finished triggered')
                self._ledsController.idle()
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On tts finished received but it wasn't for me")

        elif message.topic == self._SUB_ON_PLAY_FINISHED:
            if siteId == self._me:
                if self._params.debug:
                    self._logger.debug('On play finished triggered')
                self._ledsController.idle()
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On play finished received but it wasn't for me")

        elif message.topic == self._SUB_ON_LEDS_TOGGLE_ON:
            if siteId == self._me:
                if self._params.debug:
                    self._logger.debug('On leds toggle on triggered')
                self._ledsController.toggleStateOn()
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On leds toggle on received but it wasn't for me")

        elif message.topic == self._SUB_ON_LEDS_TOGGLE_OFF:
            if siteId == self._me:
                if self._params.debug:
                    self._logger.debug('On leds toggle off triggered')
                self._ledsController.toggleStateOff()
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On leds toggle off received but it wasn't for me")

        elif message.topic == self._SUB_ON_LEDS_TOGGLE:
            if siteId == self._me:
                if self._params.debug:
                    self._logger.debug('On leds toggle triggered')
                self._ledsController.toggleState()
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On leds toggle received but it wasn't for me")

        elif message.topic == self._SUB_LEDS_ON_SUCCESS:
            if siteId == self._me:
                if self._params.debug:
                    self._logger.debug('On success triggered')
                self._ledsController.onSuccess(sticky)
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On success received but it wasn't for me")

        elif message.topic == self._SUB_LEDS_ON_ERROR:
            if siteId == self._me:
                if self._params.debug:
                    self._logger.debug('On error triggered')
                self._ledsController.onError(sticky)
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On error received but it wasn't for me")

        elif message.topic == self._SUB_UPDATING:
            if siteId == self._me:
                if self._params.debug:
                    self._logger.debug('On updating triggered')
                self._ledsController.updating(sticky)
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On updating received but it wasn't for me")

        elif message.topic == self._SUB_ON_CALL:
            if siteId == self._me:
                if self._params.debug:
                    self._logger.debug('On call triggered')
                self._ledsController.call(sticky)
            else:
                if self._params.debug:
                    self._logger.debug("On call received but it wasn't for me")

        elif message.topic == self._SUB_SETUP_MODE:
            if siteId == self._me:
                if self._params.debug:
                    self._logger.debug('On setup mode triggered')
                self._ledsController.setupMode(sticky)
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On setup mode received but it wasn't for me")

        elif message.topic == self._SUB_CON_ERROR:
            if siteId == self._me:
                if self._params.debug:
                    self._logger.debug('On connection error triggered')
                self._ledsController.conError(sticky)
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On connection error received but it wasn't for me")

        elif message.topic == self._SUB_ON_MESSAGE:
            if siteId == self._me:
                if self._params.debug:
                    self._logger.debug('On message triggered')
                self._ledsController.message(sticky)
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On message received but it wasn't for me")

        elif message.topic == self._SUB_ON_DND:
            if siteId == self._me:
                if self._params.debug:
                    self._logger.debug('On do not disturb triggered')
                self._ledsController.dnd(sticky)
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On do not disturb received but it wasn't for me")

        elif message.topic == self._SUB_ON_START:
            if siteId == self._me:
                if self._params.debug:
                    self._logger.debug('On start triggered')
                self._ledsController.start()
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On start received but it wasn't for me")

        elif message.topic == self._SUB_ON_STOP:
            if siteId == self._me:
                if self._params.debug:
                    self._logger.debug('On stop triggered')
                self._ledsController.stop()
            else:
                if self._params.debug:
                    self._logger.debug("On stop received but it wasn't for me")

        elif message.topic == self._SUB_VOLUME_SET:
            if siteId == self._me:
                if self._params.debug:
                    self._logger.debug('On volume set triggered')
                if 'volume' not in payload:
                    self._logger.error(
                        'Missing "volume" in payload for set volume')
                else:
                    self._ledsController.setVolume(payload['volume'])
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On volume set received but it wasn't for me")

        elif message.topic == self._SUB_VADLED_SET:
            if siteId == self._me:
                if self._params.debug:
                    self._logger.debug('On vad led set triggered')
                if 'state' not in payload:
                    self._logger.error(
                        'Missing "state" in payload for set vad led')
                else:
                    self._ledsController.setVadLed(payload['state'])
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On vad led set received but it wasn't for me")

        elif message.topic == self._SUB_ON_LEDS_CLEAR:
            self._ledsController.stickyAnimation = None
            if siteId == self._me:
                if self._params.debug:
                    self._logger.debug('On leds clear triggered')
                else:
                    self._ledsController.clearLeds()
            else:
                if self._params.debug:
                    self._logger.debug(
                        "On leds clear received but it wasn't for me")

    @property
    def params(self):
        return self._params

    @property
    def hardwareReference(self) -> dict:
        return self._hardwareReference

    @property
    def hardware(self) -> dict:
        return self._hardware
    def __init__(self, params):
        self._logger = logging.getLogger('SnipsLedControl')
        self._logger.info('Initializing SnipsLedControl')

        self._mqttClient = None
        self._hardwareReference = None
        self._ledsController = None
        self._params = params

        self._snipsConfigs = self.loadConfigs()

        self._mqttServer = 'localhost'
        self._me = 'default'
        self._mqttPort = 1883
        self._mqttUsername = ''
        self._mqttPassword = ''
        self._tlsFile = ''

        self._hotwordRegex = re.compile(
            self._SUB_ON_HOTWORD.replace('+', '(.*)'))

        with open('hardware.json') as f:
            self._hardwareReference = json.load(f)
            self._logger.info('Loaded {} hardware references'.format(
                len(self._hardwareReference)))

        if params.hardware not in self._hardwareReference:
            self._logger.fatal('Trying to use an unsupported hardware')
            self.onStop()
        else:
            self._hardware = self._hardwareReference[self._params.hardware]

        if params.mqttServer is None:
            try:
                if 'snips-common' in self._snipsConfigs and 'mqtt' in self._snipsConfigs[
                        'snips-common']:
                    self._mqttServer = self._snipsConfigs['snips-common'][
                        'mqtt'].replace(':1883', '')

                    if 'mqtt_username' in self._snipsConfigs[
                            'snips-common'] and params.mqttUsername is None:
                        self._mqttUsername = self._snipsConfigs[
                            'snips-common']['mqtt_username']

                    if 'mqtt_password' in self._snipsConfigs[
                            'snips-common'] and params.mqttPassword is None:
                        self._mqttPassword = self._snipsConfigs[
                            'snips-common']['mqtt_password']

                    if 'mqtt_tls_cafile' in self._snipsConfigs['snips-common']:
                        self._tlsFile = self._snipsConfigs['snips-common'][
                            'mqtt_tls_cafile']

            except:
                self._logger.info(
                    '- Falling back to default config for mqtt server')
        else:
            self._mqttServer = params.mqttServer

        if params.clientId is None:
            try:
                if 'snips-audio-server' in self._snipsConfigs and 'bind' in self._snipsConfigs[
                        'snips-audio-server']:
                    self._me = self._snipsConfigs['snips-audio-server'][
                        'bind'].replace('@mqtt', '')
            except:
                self._logger.info(
                    '- Falling back to default config for client id')
        else:
            self._me = params.clientId

        self._SUB_ON_PLAY_FINISHED = self._SUB_ON_PLAY_FINISHED.format(
            self._me)

        if params.mqttPort is None:
            try:
                if 'snips-common' in self._snipsConfigs and 'mqtt' in self._snipsConfigs[
                        'snips-common']:
                    self._mqttPort = self._snipsConfigs['snips-common'][
                        'mqtt'].split(':')[1]
            except:
                self._logger.info(
                    '- Falling back to default config for mqtt port')
        else:
            self._mqttPort = params.mqttPort

        self._logger.info('- Mqtt server set to {}'.format(self._mqttServer))
        self._logger.info('- Mqtt port set to {}'.format(self._mqttPort))

        if self._mqttUsername:
            self._logger.info('- Mqtt username set to {}'.format(
                self._mqttUsername))
        if self._mqttPassword:
            self._logger.info('- Mqtt password set to "hidden"')

        self._logger.info('- Client id set to {}'.format(self._me))
        self._logger.info('- Hardware set to {}'.format(
            self._hardware['name']))

        string = '- Using {} as pattern with {} leds'
        if params.leds is not None:
            self._logger.info(string.format(params.pattern, params.leds))
            self._hardware['numberOfLeds'] = params.leds
        else:
            self._logger.info(
                string.format(params.pattern, self._hardware['numberOfLeds']))

        if 'gpioPin' in self._hardware:
            string = 'Using pin #{}'
            if params.gpioPin is not None:
                self._logger.info(string.format(params.gpioPin))
                self._hardware['gpioPin'] = params.gpioPin
            else:
                self._logger.info(string.format(self._hardware['gpioPin']))

        if 'vid' in self._hardware and params.vid is not None:
            self._hardware['vid'] = params.vid

        if 'gpios' in self._hardware and len(params.pureGpioPinout) > 0:
            self.hardware['gpios'] = params.pureGpioPinout

        if 'activeHigh' in self._hardware and params.activeHigh != self._hardware[
                'activeHigh']:
            self._hardware['activeHigh'] = params.activeHigh

        if 'endFrame' in self._hardware and params.endFrame is not None:
            self._hardware['endFrame'] = params.endFrame

        self._ledsController = LedsController(self)
        self._mqttClient = self.connectMqtt()
class SnipsLedControl:

    _SUB_ON_HOTWORD = 'hermes/hotword/default/detected'
    _SUB_ON_SAY = 'hermes/tts/say'
    _SUB_ON_THINK = 'hermes/asr/textCaptured'
    _SUB_ON_LISTENING = 'hermes/asr/startListening'
    _SUB_ON_HOTWORD_TOGGLE_ON = 'hermes/hotword/toggleOn'
    _SUB_LEDS_ON_ERROR = 'hermes/nlu/intentNotRecognized'
    _SUB_LEDS_ON_SUCCESS = 'hermes/nlu/intentParsed'
    _SUB_ON_PLAY_FINISHED = 'hermes/audioServer/{}/playFinished'
    _SUB_ON_TTS_FINISHED = 'hermes/tts/sayFinished'

    _SUB_ON_LEDS_TOGGLE = 'hermes/leds/toggle'
    _SUB_ON_LEDS_TOGGLE_ON = 'hermes/leds/toggleOn'
    _SUB_ON_LEDS_TOGGLE_OFF = 'hermes/leds/toggleOff'
    _SUB_UPDATING = 'hermes/leds/systemUpdate'
    _SUB_ON_CALL = 'hermes/leds/onCall'
    _SUB_SETUP_MODE = 'hermes/leds/setupMode'
    _SUB_CON_ERROR = 'hermes/leds/connectionError'
    _SUB_ON_MESSAGE = 'hermes/leds/onMessage'
    _SUB_ON_DND = 'hermes/leds/doNotDisturb'

    def __init__(self, params):
        self._logger = logging.getLogger('SnipsLedControl')
        self._logger.info('Initializing SnipsLedControl')

        self._snipsConfigs = self.loadConfigs()

        self._params = params
        self._mqttServer = 'localhost'
        self._me = 'default'
        self._mqttPort = 1883
        self._hardwareReference = None
        self._mqttClient = None
        self._ledsController = None

        with open('hardware.json') as f:
            self._hardwareReference = json.load(f)
            self._logger.info('Loaded {} hardware references'.format(
                len(self._hardwareReference)))

        if params.hardware not in self._hardwareReference:
            self._logger.fatal('Trying to use an unsupported hardware')
            self.onStop()
        else:
            self._hardware = self._hardwareReference[self._params.hardware]

        if params.mqttServer is None:
            try:
                if 'snips-common' in self._snipsConfigs and 'mqtt' in self._snipsConfigs[
                        'snips-common']:
                    self._mqttServer = self._snipsConfigs['snips-common'][
                        'mqtt'].replace(':1883', '')
            except:
                self._logger.info(
                    '- Falling back to default config for mqtt server')
        else:
            self._mqttServer = params.mqttServer

        if params.clientId is None:
            try:
                if 'snips-audio-server' in self._snipsConfigs and 'bind' in self._snipsConfigs[
                        'snips-audio-server']:
                    self._me = self._snipsConfigs['snips-audio-server'][
                        'bind'].replace('@mqtt', '')
            except:
                self._logger.info(
                    '- Falling back to default config for client id')
        else:
            self._me = params.clientId

        self._SUB_ON_PLAY_FINISHED = self._SUB_ON_PLAY_FINISHED.format(
            self._me)

        if params.mqttPort is None:
            try:
                if 'snips-common' in self._snipsConfigs and 'mqtt' in self._snipsConfigs[
                        'snips-common']:
                    self._mqttPort = self._snipsConfigs['snips-common'][
                        'mqtt'].split(':')[1]
            except:
                self._logger.info(
                    '- Falling back to default config for mqtt port')
        else:
            self._mqttPort = params.mqttPort

        self._logger.info('- Mqtt server set to {}'.format(self._mqttServer))
        self._logger.info('- Mqtt port set to {}'.format(self._mqttPort))
        self._logger.info('- Client id set to {}'.format(self._me))
        self._logger.info('- Hardware set to {}'.format(
            self._hardware['name']))

        string = '- Using {} as pattern with {} leds'
        if params.leds is not None:
            self._logger.info(string.format(params.pattern, params.leds))
            self._hardware['numberOfLeds'] = params.leds
        else:
            self._logger.info(
                string.format(params.pattern, self._hardware['numberOfLeds']))

        if 'gpioPin' in self._hardware:
            string = 'Using pin #{}'
            if params.gpioPin is not None:
                self._logger.info(string.format(params.gpioPin))
                self._hardware['gpioPin'] = params.gpioPin
            else:
                self._logger.info(string.format(self._hardware['gpioPin']))

        if 'vid' in self._hardware and params.vid is not None:
            self._hardware['vid'] = params.vid

        self._ledsController = LedsController(self)
        self._mqttClient = self.connectMqtt()

    def onStart(self):
        self._ledsController.onStart()
        self._logger.info('Snips Led Control started')

    def onStop(self):
        if self._mqttClient is not None:
            self._mqttClient.disconnect()

        if self._ledsController is not None:
            self._ledsController.onStop()

        sys.exit(0)

    def loadConfigs(self):
        self._logger.info('Loading configurations')

        if os.path.isfile('/etc/snips.toml'):
            with open('/etc/snips.toml') as confFile:
                configs = pytoml.load(confFile)
                return configs

        self._logger.fatal('Error loading configurations')
        self.onStop()
        return None

    def connectMqtt(self):
        try:
            mqttClient = mqtt.Client()
            mqttClient.on_connect = self.onConnect
            mqttClient.on_message = self.onMessage
            mqttClient.connect(self._mqttServer, int(self._mqttPort))
            mqttClient.loop_start()
            return mqttClient
        except:
            self._logger.fatal("Couldn't connect to mqtt, aborting")
            self.onStop()

    def onConnect(self, client, userdata, flags, rc):
        self._mqttClient.subscribe([(self._SUB_ON_HOTWORD, 0),
                                    (self._SUB_ON_SAY, 0),
                                    (self._SUB_ON_THINK, 0),
                                    (self._SUB_ON_LISTENING, 0),
                                    (self._SUB_ON_LEDS_TOGGLE_ON, 0),
                                    (self._SUB_ON_LEDS_TOGGLE_OFF, 0),
                                    (self._SUB_ON_LEDS_TOGGLE, 0),
                                    (self._SUB_LEDS_ON_ERROR, 0),
                                    (self._SUB_LEDS_ON_SUCCESS, 0)])

        self._mqttClient.subscribe(self._params.offListener)

    def onMessage(self, client, userdata, message):
        payload = None

        if hasattr(message, 'payload') and message.payload != '':
            payload = json.loads(message.payload)

        if payload is not None and 'siteId' in payload:
            siteId = payload['siteId']
        else:
            siteId = None

        if message.topic == self._SUB_ON_HOTWORD:
            if siteId == self._me:
                self._ledsController.wakeup()
        elif message.topic == self._SUB_ON_LISTENING:
            if siteId == self._me:
                self._ledsController.listen()
        elif message.topic == self._SUB_ON_SAY:
            if siteId == self._me:
                self._ledsController.speak()
        elif message.topic == self._SUB_ON_THINK:
            if siteId == self._me:
                self._ledsController.think()
        elif message.topic == self._SUB_ON_HOTWORD_TOGGLE_ON:
            if siteId == self._me:
                self._ledsController.idle()
        elif message.topic == self._SUB_ON_TTS_FINISHED:
            if siteId == self._me:
                self._ledsController.idle()
        elif message.topic == self._SUB_ON_PLAY_FINISHED:
            if siteId == self._me:
                self._ledsController.idle()
        elif message.topic == self._SUB_ON_LEDS_TOGGLE_ON:
            if siteId == self._me:
                self._ledsController.toggleStateOn()
        elif message.topic == self._SUB_ON_LEDS_TOGGLE_OFF:
            if siteId == self._me:
                self._ledsController.toggleStateOff()
        elif message.topic == self._SUB_ON_LEDS_TOGGLE:
            if siteId == self._me:
                self._ledsController.toggleState()
        elif message.topic == self._SUB_LEDS_ON_SUCCESS:
            if siteId == self._me:
                self._ledsController.onSuccess()
        elif message.topic == self._SUB_LEDS_ON_ERROR:
            if siteId == self._me:
                self._ledsController.onError()
        elif message.topic == self._SUB_UPDATING:
            if siteId == self._me:
                self._ledsController.updating()
        elif message.topic == self._SUB_ON_CALL:
            if siteId == self._me:
                self._ledsController.call()
        elif message.topic == self._SUB_SETUP_MODE:
            if siteId == self._me:
                self._ledsController.setupMode()
        elif message.topic == self._SUB_CON_ERROR:
            if siteId == self._me:
                self._ledsController.conError()
        elif message.topic == self._SUB_ON_MESSAGE:
            if siteId == self._me:
                self._ledsController.message()
        elif message.topic == self._SUB_ON_DND:
            if siteId == self._me:
                self._ledsController.dnd()

    @property
    def params(self):
        return self._params

    @property
    def hardwareReference(self):
        return self._hardwareReference

    @property
    def hardware(self):
        return self._hardware