Skip to content

Commit

Permalink
Improved documentation and moved most of __main__ to a config file
Browse files Browse the repository at this point in the history
  • Loading branch information
nicbou committed Sep 14, 2013
1 parent 3ae2002 commit 751fde9
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 59 deletions.
28 changes: 15 additions & 13 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Winston is a voice-activated virtual assistant meant to be used in home automati

Winston tries as hard as possible to abstract most of the steps below so you can easily create your own commands. Nonetheless, you will still need to understand a few concepts to get started. Winston works this way:

1. GStreamer splits the microphone input into *utterances* and sends them to pocketsphinx, a speech recognition framework.
1. GStreamer splits the microphone's input into *utterances* and sends them to pocketsphinx, a speech recognition framework.
2. Pocketsphinx tries to match the utterance with a command in its finite-state grammar (a list of possible commands).
3. Pocketsphinx sends the understood string to Winston's `Listener`, which simply acts as a bridge between pocketsphinx and the application.
4. The `Listener` sends the string to the `Interpreter`, which will match a command string to an actual `Command`.
Expand All @@ -28,6 +28,8 @@ The Interpreter matches raw commands such as "winston could you please open the

The Interpreter also trims sentence decorations such as "please", "could you" and "would you kindly". If you have to rename Winston, this is also where you'd do it.

A Listener can forward its data to multiple Interpreters.

### The Command

A command does two things:
Expand Down Expand Up @@ -56,25 +58,25 @@ You will also need `festival` so Winston can reply back to you. If you are using

*Important notes: Make sure you have up-to-date packages because some older versions will make your life much harder. There might be packages which I forgot about, so do not hesitate to open an issue or send an email if the instructions are incomplete or unclear. I take these to heart, and will respond as quickly as possible.*

### Creating commands
### Configuration options

You can easily add new commands to Winston:
`config.py` contains all the settings used by Winston.

1. Define your own commands by extending or instanciating the commands.Command object. You will find plenty of examples in the commands directory.
2. Add the command to your grammar file (`grammar.fsg` by default). The easiest way to do this is to edit the jsgf.txt grammar, then convert it to .fsg using `sphinx_jsgf2fsg`.
3. Add your command to the Interpreter in `run.py`.
**`COMMANDS`** contains a list of Commands used by the Interpreter.

There is a file called run.py that will run winston from the command line. Make sure to open it as it contains simple examples that will show you how to define commands. By default, run.py uses grammar.fsg, and will recognize these commands. Feel free to define your own grammar and commands.
**`SCHEDULER`** is the scheduler object that stores timed and repeating events set by commands.

### Running Winston

Once installed, run winston by running `python winston/run.py`. You may also use it in your applications by importing the required modules in your own project. You will find pretty intuitive examples by opening `run.py`.
### Creating commands

You can easily add new commands to Winston:

## Creating commands
1. Define your own commands by extending or instanciating the commands.Command object. You will find plenty of examples in the commands directory.
2. Add the command to your grammar file (`grammar.fsg` by default, see `config.py`). The easiest way to do this is to edit the `jsgf.txt` grammar, then convert it to .fsg using `sphinx_jsgf2fsg`. Make sure you use words that are already present in the Interpreter's dictionary.
3. Add your command to the COMMANDS list in `config.py`.

Commands can be created by extending the Command object, or by instanciating it, depending on the complexity of your commands.
Make sure to open `config.py` it contains simple examples that will show you how to define commands. It also defines the path to your dictionary and grammar files.

You will also need to generate the grammar on your own, since the commands do not automatically translate to a JSGF grammar. There is an included grammar, `jsgf.txt` that is converted to `grammar.fsg`.
### Running Winston

*Note: Instructions unclear? Open a ticket and I will get back to you very quickly. Publishing this code was an afterthought.*
Once installed, run winston by running `python winston/`. You may also use it in your applications by importing the required modules in your own project. Look at `__main__.py` to see how Winston is started, and what classes come into play.
50 changes: 7 additions & 43 deletions winston/__main__.py
Original file line number Diff line number Diff line change
@@ -1,57 +1,21 @@
from listener import Listener
from interpreter import *
from commands import Command
from commands.say import SayCommand, sayTime
from commands.open_door import OpenDoorCommand
from commands.set_alarm import AbsoluteAlarmCommand, RelativeAlarmCommand
from commands.activate import ActivateCommand
from commands.deactivate import DeactivateCommand
from commands.account_balance import AccountBalanceCommand, say_balance
from commands.next_bus import NextBusCommand
from commands.dinner import DinnerCommand
from apscheduler.scheduler import Scheduler
import os
import config

def main():
"""
Allows Winston to be installed as a package and run from the command line
Allows Winston to be installed as a package and to be run from the command line
"""

# This file can be called from the command line, and will run Winston
# The grammar.fsg is a finite state grammar file generated from jsgf.txt
# using sphinx_jsgf2fsg. It helps the listener associate words to the correct
# commands.
script_path = os.path.dirname(__file__)
grammar_file = os.path.join(script_path, "grammar.fsg")
dict_file = os.path.join(script_path, "dict.dic")

# The list of commands passed to the interpreter
commands = [
# Commands defined by extending the Command object. These are a few examples.
SayCommand(), # Simple command to get started
ActivateCommand(), # Can activate winston
DeactivateCommand(), # Can deactivate winston
AccountBalanceCommand(), # Lots of variation, uses regex actions
OpenDoorCommand(),
AbsoluteAlarmCommand(),
RelativeAlarmCommand(),
NextBusCommand(),
DinnerCommand(),
]

# A command defined by instanciating the Command object
commands.append(Command(name='whatTime', actions=('what time is it',), callback=sayTime))

# Define and start a scheduler. These store tasks that are run at given times
scheduler = Scheduler()
scheduler = config.SCHEDULER
scheduler.start()
scheduler.add_cron_job(say_balance, hour=18, minute=00) # Reads the account balance at 17:30, daily

# Load the commands in the interpreter
interpreter = Interpreter(commands=commands, scheduler=scheduler)
# Load the commands in the interpreter. These dispatch commands. See the Interpreter's doc for details.
interpreter = Interpreter(commands=config.COMMANDS, scheduler=config.SCHEDULER)

# Get a listener. The grammar argument is optional, see Listener's doc for details
listener = Listener(interpreters=[interpreter], fsg_path=grammar_file, dict_path=dict_file)
# Create a listener for pocketsphinx. It forwards recognized strings to Interpreters. See Listener's doc for details.
listener = Listener(interpreters=[interpreter], fsg_path=config.GRAMMAR_FILE, dict_path=config.DICT_FILE)

# And wait...
raw_input()
Expand Down
31 changes: 30 additions & 1 deletion winston/config.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Winston configuration file
# Read the documentation to learn more about the settings below

from apscheduler.scheduler import Scheduler
import os

# The path to the arduino's serial device
ARDUINO_PATH = '/dev/ttyACM0'
ARDUINO_BAUD_RATE = 9600
Expand All @@ -6,4 +12,27 @@
BUS_SCHEDULE_URL = "http://i-www.stm.info/en/lines/{line}/stops/{stop}/arrivals?direction={direction}&limit=5"

# The path to the balance file generated by an external script
BALANCE_PATH = "/var/www/scripts/winston_balance.txt"
BALANCE_PATH = "/var/www/scripts/winston_balance.txt"

# Define your own commands and add them here
from commands import say, activate, deactivate, account_balance, open_door, set_alarm, next_bus, dinner, Command
COMMANDS = [ # The list of commands passed to the interpreter
say.SayCommand(), # Simple command to get started
activate.ActivateCommand(), # Can activate winston
deactivate.DeactivateCommand(), # Can deactivate winston
account_balance.AccountBalanceCommand(), # Lots of variation, uses regex actions
open_door.OpenDoorCommand(),
set_alarm.AbsoluteAlarmCommand(),
set_alarm.RelativeAlarmCommand(),
next_bus.NextBusCommand(),
dinner.DinnerCommand(),
Command(name='whatTime', actions=('what time is it',), callback=say.sayTime), # A simple command
]

# Define a scheduler to store scheduled events
SCHEDULER = Scheduler()

# The path to the grammars used by pocketsphinx
SCRIPT_PATH = os.path.dirname(__file__)
GRAMMAR_FILE = os.path.join(SCRIPT_PATH, "grammar.fsg")
DICT_FILE = os.path.join(SCRIPT_PATH, "dict.dic")
5 changes: 3 additions & 2 deletions winston/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ def __init__(self, commands, scheduler=None):
"""
Prepares the interpreter, compile the regex strings
"""
# Keep a reference to the scheduler so commands can use it
self.scheduler = scheduler

# Keep a reference to the scheduler
self.scheduler = scheduler

# Keep a reference to the interpreter, give command a unique name
for index, command in enumerate(commands):
command.interpreter = self
Expand Down

0 comments on commit 751fde9

Please sign in to comment.