Skip to content

Plugin Developer Guide

elvisimprsntr edited this page Mar 15, 2013 · 32 revisions

So you want to write a plugin?! You came to the right place!

Modify the default plugin

The easiest way to create a plugin is to modify the existing one. Open this file:

plugins/siriproxy-example/lib/siriproxy-example.rb

You can see a simple structure of how Siri intercepts voice commands. The syntax is:

listen_for /regular expression/i do
    say something
    request_completed
end

When you speak to Siri, it matches the captured text against the regular expressions you specify with each "listen_for". See later in this document for some Ruby regular expression tricks if you aren't familiar with Ruby syntax.

Try changing this line:

  listen_for /test siri proxy/i do

to:

  listen_for /hello siri proxy/i do

Installing your plugin

After modifying the default plugin, stop your Siri server (Ctrl-C). In your Siri root directory (not .siriproxy, but the root of your Siri proxy checkout) run:

siriproxy update .

The '.' is required because otherwise the update will go out to Github to find things. This isn't documented anywhere, so hopefully you found this page!

Start up your Siri proxy server again with:

rvmsudo siriproxy server

Now test it out! Use your phone to say "hello siri proxy". If you get the correct response, it works! Congratulations!

How to add other plugins

It depends on a number of factors: SP installed as root or user, SP installed as a gem or cloned, and the instructions for the particular plugin. There are also a number of ways to add plugins. For example, if everything is installed as root and SiriProxy is installed as a Ruby gem, you can do the following:

  1. For plugins which tell you to add git: 'git://github.com/... to the config.yml file, simply add the plugin specifics to the /root/.siriproxy/config.yml file.
  2. For plugins which tell you to add path: './plugins/... to the config.yml file, there are a couple of ways to do this:
    • The hard way is to add the plugin specifics to the /root/.siriproxy/config.yml file, clone/download the plugin repo, and copy to or create a symbolic link in the /usr/local/rvm/gems/ruby-2.0.0-p#/gems/siriproxy-0.5.#/plugins/ directory. Note: Replace #'s as appropriate, and make sure to name the plugin directory or symbolic link the same as specified in the config.yml file.
    • The easy way is to simply clone/download the plugin repo to a /root/plugins directory, optionally create symbolic links in the /root/plugins/ directory, then explicitly define the entire path in the config.yml file. Note: This may break some plugins that use a file path for other cached or configuration files. You may need to tweak those plugins to look for those files in the appropriate directory.
  3. Then, perform siriproxy bundle and launch SiriProxy again.

Publishing your plugin for others to install

You can post to GH using the old school command line method, or you can simply download the native app for your OS distribution which makes it 1000X easier to mange your GH repos.  The easiest way is to clone an existing repo and tweak from there.  

http://windows.github.com/help.html
http://mac.github.com/help.html

Also edit Plamoni's SiriProxy plugins page to add links to your repo and demo videos.  

https://github.com/plamoni/SiriProxy/wiki/Plugins

Regex tips

Ruby regexes are similar to other languages. If you are not familiar with a regular expression, go read up on it, because you won't be able to develop a plugin without that knowledge.

To specify a capture group:

listen_for /siri proxy word ([a-z]*)/i do |word|

To specify multiple capture groups (test this by saying "siri proxy word hello number three"):

listen_for /siri proxy word ([a-z]*) number ([a-z]*)/i do |word, number|

SUPER IMPORTANT NOTE: Why did we make "number" a letter group instead of digits? It's because nothing magic happens when you say a number. Siri will interpret you saying "three" as the word "three". The exception to this is if you say a string of numbers like "four five six" Siri will interpret that as "456".

To specify optional words:

listen_for /how do I get to(?: the)? store/i do

There are two important things in the above statement. First, the ?: in the group means it will not capture, so you don't need to add a |capture| after the do. If you want to capture the optional word, remove the ?:. The second important thing is the space is IN the capture group. If you just did this:

listen_for /how do I get to (?:the)? store/i do

And you said "how do I get to store" to Siri, it wouldn't match, because the regex would be looking for two spaces, one before the group and one after. Tricky and stupid stuff!

Passing configuration options to your plugin

You can pass configuration options from the config.yml file as individual parameters or as a Hash. For example, passing in a Hash of multiple key/value pairs, and corresponding check for missing options. (Source: https://github.com/elvisimprsntr/siriproxy-ipcam)

Example config.yml

- name: 'IPCam'
  path: './plugins/siriproxy-ipcam'
  camurls: { 'garage': 'http://192.168.69.81/cgi/jpg/image.cgi', 
             'porch': 'http://192.168.69.82/cgi/jpg/image.cgi', 
             'roof': 'http://192.168.69.83/cgi/jpg/image.cgi', 
             'living room': 'http://192.168.69.85/Jpeg/CamImg.jpg' } #URLs of IP camera image.
  camid: 'username'
  campw: 'password'
  webip: 'http://192.168.69.95' #IP address of web server to cache camera images

Example code from plugin.

 class SiriProxy::Plugin::IPCam < SiriProxy::Plugin
   attr_accessor :camurls
   attr_accessor :camid
   attr_accessor :campw
   attr_accessor :webip

   def initialize(config = {})
     @camUrl = Hash.new
     @camUrl = config["camurls"]
     @camAuth = nil
     @camAuth = {:http_basic_authentication => [config["camid"], config["campw"]]} if config["camid"] 
     @webIp = config["webip"] 
   end

Making Siri remember things

One way to get Siri to remember things is to cache them to a file which is restored upon reinitialization of SiriProxy. For example, if you have multiple key/value pairs you want Siri to remember you can store them in a Hash and write/read to/from a CSV file. (Source: https://github.com/elvisimprsntr/siriproxy-redeye)

 require 'csv' 

 # Read Hash from a CSV file:
 @reFile = "#{Dir.home}/.siriproxy/resel.csv"
 @reSel = Hash.new
 if File.exists?(@reFile)
 	@reSel = Hash[CSV.read(@reFile)]
 else
 	@reSel["redeye"] = "living"
 	@reSel["room"] = "living"
 	@reSel["device"] = "tv"
 	@reSel["feed"] = "ota"
 end
 
 # Write Hash to a CSV file:
 	CSV.open(@reFile, "wb") {|csv| @reSel.to_a.each {|elem| csv << elem} }

Using special characters

If you want to use special characters, such as accents, some modifications are needed (see Issue #50). Basically, you have to add the line below at the first line of any .rb file (don't panic, there's a patch for that ;) )

 # -*- encoding: utf-8 -*-

Also, your text editor may be configured to save files using UTF-8. Here's the command if you want to patch SiriProxy's .rb files

curl -o- https://github.com/eMxyzptlk/SiriProxy/commit/739b0cefb3e60b94514123c010820a1d51c99dfb.patch | patch -Np1

Troubleshooting

If you get this when starting up Siri proxy:

rescue in block in initialize': Cannot start the server on port 443

Run this command to find out which programs are listening on port 443:

sudo lsof -i:443

Then get their PIDs (you probably only need to kill ruby) from the PID column and kill them with:

sudo kill -9 xxxx

This page is a work in progress. Feel free to edit it.