Now each plugin base class (PluginBase and Handler) has an abstract
method for the main functionality it provides, forcing plugin authors to
implement those methods before their plugin can be successfully used.
Now that I've freed myself from Pythons older than 3.6, I can use
f-strings for very clear and concise string formatting! That's now how
it's done everywhere except one place in the shell plugin where
str.format is really the right thing to do.
Yeah, I don't feel like setting up multiple Pythons for Pocketsphinx and
making a real test suite to ensure everything is working for 3.5. I'm
planning on using provisional APIs (specifically the `typing` module) so
I won't make any claim to support any version of Python other than the
one I'm developing with.
This one got a lot more verbose, but I like it more too. The caching
and formatting methods were moved to a base Handler class, and
individual commands subclass this using its methods as needed. The
"hoos your weather provider" command doesn't even update the cache
anymore, which is nice.
There's still no support for telling Kaylee that a handler failed, so
errors pass silently now. The plugin got much more long-winded, but at
least we only have to get the MPRIS proxy object once instead of three
separate times like we did before.
The new shell plugin is a lot cleaner, only determining how the command
matches once and parsing numbers zero or one times.
Kaylee uses the new API, but there are still some problems. Most
notably, Handler objects have no sorting methods yet, so behavior will
be unpredictable when multiple plugins are loaded.
This gives the darksky plugin a temp_precision option, which allows
users to set the precision with which temperatures are read. It
defaults to 0, not the old hard-coded value of 1, because I decided that
it's not usually useful to hear that much information.
Now setting the minimum_confidence option in options.json allows the
user to control the minimum confidence a plugin must have to be executed
at all. A value of null (the default) means all values greater than
zero are accepted. All other values mean all confidences greater than
or equal to that value are accepted.
As described in issue #16, commands are now processed in two stages.
First, all plugins get to give a confidence with which they are the
right plugin to handle the command. Then, the one with the highest
confidence gets to run first, and if for some reason it can't handle the
command, other plugins try in turn.
Previously, the default configuration file was only provided in a form
that was not included in distributions. This could potentially
frustrate users by making it difficult to find a configuration file
example. To remedy this, I moved it to be a package resource, imported
pkg_resources (setuptools is now a dependancy!), and made Kaylee load
both the default and the local configuration files, with the local of
course overriding the default.
Now you can control media players supporting MPRIS using Kaylee. Just
load the plugin and commands like "play the music", "pause the video",
"previous song", and "next video" magically work.
The cache time is now configurable by the user. If no cache time is
set, the old default of half an hour is still used.
Temperature formatting has been factored out into its own function.
There's no reason that continuous_stop should be a different command
from stop. Sending a stop command when in continuous listen mode would
confuse matters, so preventing that from happening at all is beneficial.
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.