def is_option_list(value, *args):
def initConfig(path='config.ini'): class Controller(threading.Thread):
    """Threaded controller that holds the main logic of Dobby. It grabs events as they come and put corresponding
    (according to the database) processed actions in the queue.
    Error message and confirmation messages are customizable

    :param Queue.Queue event_queue: where to listen for events
    :param Queue.Queue tts_queue: where to put the tts from processed actions
    :param Session session: Dobby database session
    :param integer recognition_timeout: time to wait for a :class:`~dobby.models.command.Command` to be recognized once a :class:`RecognitionEvent` is received
    :param string failed_message: error message to say when the recognized :class:`~dobby.models.command.Command` does not match anything in the database
    :param list confirmation_messages: a random message to say is picked and sent to the action queue whenever a :class:`RecognitionEvent` is caught

    """
    def __init__(self, event_queue, tts_queue, session, recognizer, recognition_timeout, failed_message, confirmation_messages):
        super(Controller, self).__init__()
        self.event_queue = event_queue
        self.tts_queue = tts_queue
        self.session = session
        self.recognizer = recognizer
        self.recognition_timeout = recognition_timeout
        self.failed_action = Action(tts=failed_message)
        self.confirmation_messages = confirmation_messages
        self._stop = False

    def stop(self):
        """Stop the thread"""
        self._stop = True

    def run(self):
        while not self._stop:
            # Get the event from the queue
            try:
                event = self.event_queue.get(timeout=1)
            except Queue.Empty:
                continue
            
            # Fire a Scenario directly if the event is an CommandEvent
            if isinstance(event, CommandEvent):
                logger.debug(u'CommandEvent caught with command "%s"' % event.command)
                scenario = self.session.query(Scenario).join(Command).filter(Command.text == event.command).first()

            # Launch recognition and analyze the first voice command
            elif isinstance(event, RecognitionEvent):
                # Send a random confirmation message
                if self.confirmation_messages:
                    self.tts_queue.put(random.sample(self.confirmation_messages, 1)[0])
                # Monitor the recognized voice commands and catch the first one
                recognition_queue = Queue.Queue()
                self.recognizer.subscribe(recognition_queue)
                try:
                    recognition = recognition_queue.get(timeout=self.recognition_timeout)
                    recognized_command = unicode(recognition)
                    logger.debug(u'RecognitionEvent caught with voice command "%s"' % recognized_command)
                    scenario = self.session.query(Scenario).join(Command).filter(Command.text == recognized_command).first()
                except Queue.Empty:
                    scenario = None
                    pass
                self.recognizer.unsubscribe(recognition_queue)

            # Failed message if no scenario
            if not scenario:
                logger.debug(u'Could not find the scenario in database')
                if self.failed_action.tts:
                    self.tts_queue.put(self.failed_action.format_tts())
                continue

            # Loop over the scenario's actions in the correct order and execute them
            for action_number in sorted(scenario.actions.keys()):
                self.tts_queue.put(scenario.actions[action_number].format_tts())

            # Mark the task as done
            self.event_queue.task_done()
            
            # Expire session
            self.session.expire_all()
        logger.info(u'Terminating...') def initDb(path):
    """Initialize database (create/update) and returns a sessionmaker to it

    :return: a session maker object
    :rtype: SessionMaker

    """
    logger.info(u'Initializing database')
    engine = create_engine('sqlite:///' + path)
    if not os.path.exists(path):
        logger.debug(u'Database does not exist, creating...')
        Base.metadata.create_all(engine)
    return sessionmaker(bind=engine) class Julius(Recognizer):
    """Julius Recognizer is based on `Julius speech recognition engine <http://julius.sourceforge.jp/en/>`_.
    It uses :mod:`pyjulius` to connect to julius instance running in module mode
    
    :param string host: host of the server
    :param integer port: port of the server
    :param string encoding: encoding used to decode server's output
    :param float min_score: minimum score under which the recognition result will be ignored

    """
    def __init__(self, host, port, encoding, min_score):
        super(Julius, self).__init__()
        self.client = pyjulius.Client(host, port, encoding)
        self.min_score = min_score

    def run(self):
        """Run the recognition and :meth:`~dobby.recognizers.Recognizer.publish` the recognized :class:`pyjulius.Sentence` objects"""
        self.client.connect()
        self.client.start()
        while not self._stop:
            try:
                result = self.client.results.get(timeout=1)
            except Queue.Empty:
                continue
            if not isinstance(result, pyjulius.Sentence):
                continue
            sentence = unicode(result)
            score = abs(result.score)
            if self.min_score and score < self.min_score:
                logger.debug(u'Rejected sentence "%s" with score %f < %f' % (sentence, score, self.min_score))
                continue
            logger.debug(u'Firing recognition "%s" with score %f' % (sentence, score))
            self.publish(result)
        logger.info(u'Terminating...')
        self.client.stop()
        self.client.join()
        self.client.disconnect() class SpeechDispatcher(Speaker):
    """Speaker using speech-dispatcher (speechd)

    :param string name: speechd's name of the SSIPClient
    :param string engine: speechd's output module
    :param string voice: speechd's voice
    :param string language: speechd's language
    :param integer rate: speechd's rate
    :param integer pitch: speechd's pitch

    """
    def __init__(self, tts_queue, name, engine, voice, language, volume, rate, pitch):
        super(SpeechDispatcher, self).__init__(tts_queue)
        self.client = speechd.SSIPClient(name)
        self.client.set_output_module(engine)
        self.client.set_voice(voice)
        self.client.set_language(language)
        self.client.set_volume(volume)
        self.client.set_rate(rate)
        self.client.set_pitch(pitch)
        self.client.set_punctuation(speechd.PunctuationMode.SOME)

    def speak(self, text):
        logger.debug(u'Speaking "%s"' % text)
        self.client.speak(text, callback=self._callback, event_types=(speechd.CallbackType.END))
        self.state = SPEAKING
        self._wait()

    def _callback(self, event_type):
        """Callback for speechd end of speech

        :param speechd.CallbackType event_type: type of the event raised by speechd

        """
        if event_type == speechd.CallbackType.END:
            self.state = IDLE

    def _wait(self, timeout=60, poll=0.1):
        """Block until :attr:`state` changes back to :data:`IDLE`

        :param integer timeout: maximum time to wait
        :param double poll: polling interval for checking :attr:`state`

        """
        i = 0
        while self.state == SPEAKING and i <= timeout / poll:
            time.sleep(poll)
            i += 1

    def terminate(self):
        logger.debug(u'Terminating...')
        self.client.close() class Pattern(list):
    """Enhanced list that is used to match a :class:`Sequence`"""
    def match(self, sequence):
        if len(sequence) != len(self):
            return False
        for i in range(len(sequence)):
            if not self[i].match(sequence[i]):
                return False
        return True class QuietPattern(PatternItem):
    """A QuietPattern validates a :class:`QuietBlock`"""
    validate = QuietBlock class NoisyPattern(PatternItem):
    """A NoisyPattern validates a :class:`NoisyBlock`"""
    validate = NoisyBlock class Clapper(Trigger):
    """Analyze an audio source and put a :class:`~dobby.models.recognizers.RecognitionEvent` in the queue
    if the resulting :class:`Sequence` of :class:`Block` matches the :class:`Pattern`

    :param integer device_index: device index as given per :class:`pyaudio.PyAudio`
    :param Pattern pattern: pattern that has to be matched to :meth:`~dobby.models.recognizers.Recognizer.raise_event` the event
    :param float threshold: level of input sound
    :param integer channels: number of channels for the input
    :param integer rage: rate of the input (in Hz)
    :param float block_time: length of audio input chunks (in seconds)

    """
    def __init__(self, event_queue, device_index, pattern, threshold, channels, rate, block_time):
        super(Clapper, self).__init__(event_queue)
        self.device_index = device_index
        self.pattern = pattern
        self.threshold = threshold
        self.channels = channels
        self.rate = rate
        self.block_time = block_time

    def run(self):
        # Init the stream
        pa = pyaudio.PyAudio()
        chunk = int(self.rate * self.block_time)
        stream = pa.open(format=pyaudio.paInt16, channels=self.channels, rate=self.rate,
                         input=True, input_device_index=self.device_index, frames_per_buffer=chunk)
        threshold = self.threshold
        sequence = Sequence(maxlen=len(self.pattern))
        # Main loop
        while not self._stop:
            data_block = stream.read(chunk)
            rms = self.get_rms(data_block)
            # Detect the type of block
            if rms > threshold:
                sequence.append(NoisyBlock())
            else:            
                sequence.append(QuietBlock())
            # Trigger an event and reset if the sequence matches the pattern
            if self.pattern.match(sequence):
                logger.debug(u'Pattern matched the sequence %r' % sequence)
                self.raise_event(RecognitionEvent())
                sequence.clear()
        # Close
        stream.close()
        pa.terminate()


    def get_rms(self, data_block):
        """Root Mean Square of a block

        :param data_block: data block as read from the audio input

        """
        count = len(data_block) / 2
        shorts = struct.unpack('%dh' % count, data_block)
        # Iterate over the block to compute the sum of squares
        sum_squares = 0.0
        for sample in shorts:
            # Sample is a signed short in +/- 32768, normalize it to +/- 1.0
            n = float(sample) / 32768
            sum_squares += n * n
        return math.sqrt(sum_squares / count) class Julius(Trigger):
    """Analyze an audio source and put an event in the queue if voice command is spoken

    :param string command: voice command to match
    :param Recognizer recognizer: :class:`Julius Recognizer <dobby.recognizers.julius.Julius>` instance 
    :param boolean action: whether to fire :class:`CommandEvents <dobby.triggers.CommandEvent>` or not

    """
    def __init__(self, event_queue, command, recognizer, action):
        super(Julius, self).__init__(event_queue)
        self.command = command
        self.recognizer = recognizer
        self.action = action

    def run(self):
        recognition_queue = Queue.Queue()
        self.recognizer.subscribe(recognition_queue)
        while not self._stop:
            try:
                recognition = recognition_queue.get(timeout=1)
            except Queue.Empty:
                continue
            recognized_command = unicode(recognition)
            if recognized_command == self.command:
                logger.debug(u'Firing RecognitionEvent')
                self.event_queue.put(RecognitionEvent())
                continue
            if self.action and recognized_command.startswith(self.command):
                command = recognized_command[len(self.command) + 1:]
                logger.debug(u'Firing CommandEvent("%s")' % command)
                self.event_queue.put(CommandEvent(command))
                continue
            logger.debug(u'Reject recognition "%s"' % (recognized_command))
        self.recognizer.unsubscribe(recognition_queue)
        logger.info(u'Terminating...')
from Queue import Queue
from configobj import flatten_errors
from dobby.config import initConfig, validator
from dobby.controller import Controller
from dobby.db import initDb
from dobby.recognizers.julius import Julius as JuliusRecognizer
from dobby.speakers.speechdispatcher import SpeechDispatcher
from dobby.triggers.clapper import Pattern, QuietPattern, NoisyPattern, Clapper
from dobby.triggers.julius import Julius as JuliusTrigger
from sqlalchemy.orm import scoped_session
import atexit
import logging.handlers
import os
import signal
import sys
import time
        initLogging(self.quiet or self.daemon, self.verbose, os.path.join(self.datadir, 'logs'))
        self.Session = scoped_session(initDb(os.path.join(self.datadir, 'dobby.db')))
        self.running = False

    def daemonize(self):
        """Do the UNIX double-fork magic, see Stevens' "Advanced
        Programming in the UNIX Environment" for details (ISBN 0201563177)
        http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16

        """
        try:
            pid = os.fork()
            if pid > 0:  # exit first parent
                sys.exit(0)
        except OSError, e:
            sys.stderr.write('Fork #1 failed: %d (%s)\n' % (e.errno, e.strerror))
            sys.exit(1)

        # decouple from parent environment
        os.chdir('/')
        os.setsid()
        os.umask(0)

        # do second fork
        try:
            pid = os.fork()
            if pid > 0:  # exit from second parent
                sys.exit(0)
        except OSError, e:
            sys.stderr.write('Fork #2 failed: %d (%s)\n' % (e.errno, e.strerror))
            sys.exit(1)

        # write pidfile
        atexit.register(self.delpid)
        file(self.pidfile, 'w+').write('%s\n' % str(os.getpid()))

    def delpid(self):
        os.remove(self.pidfile)
    
    def start(self):
        """Start the application (and daemonize if necessary)"""
        file(self.pidfile, 'w+').write('%s\n' % str(os.getpid()))
        if self.daemon:
            self.daemonize()
        self.run()

    def stop(self):
        """Stop the application"""
        if not self.running:
            return
        if self.config['General']['bye_message']:
            self.tts_queue.put(self.config['General']['bye_message'])
        self.controller.stop()
        self.controller.join()
        for trigger in self.triggers:
            trigger.stop()
            trigger.join()
        self.recognizer.stop()
        self.recognizer.join()
        self.speaker.stop()
        self.speaker.join()
        self.config.write()
        self.running = False

    def run(self):
        self.running = True
        self.recognizer = initRecognizer(self.config['Recognizer'])
        self.recognizer.start()
        self.triggers = initTriggers(self.event_queue, self.recognizer, self.config['Trigger'])
        for trigger in self.triggers:
            trigger.start()
        self.speaker = initSpeaker(self.tts_queue, self.config['Speaker'])
        self.speaker.start()
        self.controller = initController(self.event_queue, self.tts_queue, self.Session(), self.recognizer, self.config['General'])
        self.controller.start()

        # Welcome message
        if self.config['General']['welcome_message']:
            self.tts_queue.put(self.config['General']['welcome_message'])

        # Plug signals
        if self.use_signal:
            signal.signal(signal.SIGINT, self.signal_handler)
            signal.signal(signal.SIGTERM, self.signal_handler)
        
        # Wait until it's time to exit
        while self.running:
            time.sleep(1)
        #TODO: Error handling of prematurate child death

    def signal_handler(self, *args):
        logger.info(u'Stop signal caught')
        self.stop()
        exit(0)

    def write_default_config(self):
        self.config.validate(validator, copy=True)
        self.config.write()

    def validate_config(self):
        results = self.config.validate(validator, copy=True)
        if results != True:
            return flatten_errors(self.config, results)
        return True


def initTriggers(event_queue, recognizer, config):
    """Initialize all triggers as defined in the config

    :param Queue.Queue event_queue: where event will be raised into
    :param Recognizer recognizer: recognizer instance (only :class:`~dobby.recognizers.julius.Julius` is supported now)
    :param dict config: triggers-related settings
    :return: started triggers
    :rtype: list of Trigger

    """
    logger.debug(u'Initializing triggers')
    triggers = []
    for trigger_name in config['triggers']:
        if trigger_name == 'clapper':

            pattern = Pattern([QuietPattern(1), NoisyPattern(1, 4), QuietPattern(1, 6), NoisyPattern(1, 4), QuietPattern(1)])