It has the same functionality as my old shell script, but now it's
written in Python. The code is far from the most elegant it could be,
but it's a decent example of how the new plugin API can be used for
something practical.
Finally, some sort of built-in TTS support. When a plugin emits a "tts"
signal, the Kaylee object will receive it and speak the given text
aloud. It even stops listening while it speaks to prevent Kaylee from
talking to herself. If no TTS is configured, it will print the text
instead, but since a default TTS setting is provided in the new
options.json.tmp, that shouldn't happen much.
Currently there's no way for the shell plugin to use TTS. The D-Bus
interface will change that once I get around to making it. Speaking of
D-Bus, UIs are broken again and I'm sure I can fix them once they're
separate processes talking to Kaylee by D-Bus.
After listening to HPR 1284 today, I realized that it's important to
keep configuration of what Kaylee *does* separate from other
configuration. Specifically, once TTS integration is done, voice
configuration will need to be separate from configuration of actions,
because it's common to have several computers with the same actions but
different voices. Therefore, I've moved all configuration about plugins
into a new file called plugins.json.
To reduce the boilerplate code needed for each plugin, the PluginBase
class now pulls out the plugin's options from the configuration object.
They get stored under the reasonably obvious name of ``options``.
The abstract method really just wasn't working at all. Since it doesn't
really hurt anything to be able to instantiate PluginBase, I just
forewent all the metaclass nonsense and made it a normal class.
There's no more pass_words option, so we shouldn't mention that anymore.
Also, commands aren't in a configuration section called "commands"
anymore, so use the new name, ".shell".
Previously, the order of elements in dicts in the configuration file was
not retained. The most notable consequence of this is that plugins
would be loaded in an arbitrary order, which may have led to unexpected
or inconsistent results. Now order is retained, so plugins will be
loaded in the order specified, and all is well in the world.
Now the Kaylee object handles the invalid_sentence_command as well. It
does this by having the last handler for the recognizer's 'finished'
signal. If this handler is ever run, the voice command was obviously
not wanted by any of the plugins, so the invalid_sentence_command should
be run.
Also, I fixed the UIs to work as before. It's a temporary fix!
The plugins shouldn't be in charge of running the
valid_sentence_command. They also certainly shouldn't have to manage
the history file. Both of these functions have been delegated to the
Kaylee object, by way of a 'processed' signal emitted by a plugin as
soon as it knows it will handle the voice command.
Since Kaylee now supports Python plugins that can do arbitrary things
with spoken words, the pass_words option for shell commands seems
somewhat less necessary than before. I never liked it in the first
place, because it was all-or-nothing: there was no non-hackish way to
pass the spoken words to only specific commands. If I ever see a
compelling reason for this feature to come back, it may be
re-implemented in the form of a second shell plugin that passes words to
its commands.
As part of the effort for resolving #12, I've started work on a plugin
API for Kaylee. While very much a work in progress, it will allow
Python plugins to be written, loaded from user configuration, and
hooked in to events from necessary portions of Kaylee to handle voice
commands.
Currently there is only one plugin, a partial implementation of shell
command support as existed previously. It works in that it executes
commands, but several old features are missing. Also, the GUIs are
probably broken, but I'm not worried about that at the moment.
I spelled it "fourty", which tricks the lmtool into making it much
harder to recognize the word. Now it's spelled "forty", which makes it
much easier to understand.
This makes pocketsphinx pick up way fewer false-positives of single
number-words as recognised sentences. It doesn't seem to make any
difference in anything else, but fewer false-positives is always nice.
It actually does do the things claimed in the first sentence, so don't
use the future tense.
There's no need to tell the user the details of intermediate steps in
generating the language. All the user needs to know is that it uses the
network to update the language model when it starts.
They still need work of course, since I'm a programmer and not an
artist. The new icons of course are Kaylee's parasol, and anyone who
doesn't know that needs to watch Firefly.
Commands aren't stored in a special file anymore, and Kaylee never tries
to look at that file. Therefore, there's no need for the Config object
to keep that file's path, and so the path has been removed.
Added some blank lines and wrapped some long lines to be closer to PEP 8
compliance.
Improved docstrings in NumberParser class. Added docstrings for
undocumented code and removed a TODO message that no longer applies.
Now distributions created from setup.py put the systemd unit file in the
correct location for units provided by packages. It may then be enabled
and started by the user with systemctl.
Closes #11.
Next I need to make the setup.py script install it. It's meant to be a
user service (installed to /usr/lib/systemd/user/), since Kaylee
currently does not support being run outside of a normal user account.
Also, it would be really nice to make it possible to reload the service
once it has been started. This will require some changes to Kaylee to
support synchronously reloading configuration. I understand that you
aren't supposed to reload units asynchronously, and I *really* can't
trust Kaylee to reload succinctly since reloading configuration may
require contacting a remote server until #10 is implemented.
Configuration reloading may be a separate issue from the systemd unit
file, but I'd like to see it by version 0.2.
Apparently CMU thinks it's a good idea to release a new version of a
piece of software that breaks existing APIs without even changing the
version number. I find this idea to be highly dubious at best.
Nevertheless, I have updated Kaylee to support the latest version of
pocketsphinx.
Now we check a hash of the voice commands before writing the strings
file to reduce how much we write to the hard disk. In implementing
this, I realized that some code was being duplicated in an easily
fixable way, so I created a Hasher object that keeps track of the
hash.json file.
Resolves #6
Its control flow was confusing before; now it's much more
straightforward. We make a string representing classes of words, split
that by a regular expression for number words, then parse each number
and build up our return string and list. It works just as well as the
previous method, is a bit shorter, and I feel that it's clearer as well.
It wasn't actually guaranteed, it turns out. I was iterating over
dictionary keys, which is done in arbitrary order. The result was that
in different executions of the program, the corpus was generated
differently, so the hashes differed, and the language had to be updated.
Sorting the keys before adding them to the list of number-words fixed
the problem.
See commands.tmp for an example. It's pretty neat, but it could still
use some work. I thought of a really clever way to parse numbers, better
than the one I came up with last night, but since I have a working
implementation now I figure I'd better commit it.
We have a new bug which causes the dictionary to be updated every time the
program starts. I hope I didn't force that to happen last night or
something, but I have a vague feeling I did.