SiriProxy plugin for Indigo

Posted on
Mon Oct 15, 2012 12:33 am
mreyn2005 offline
User avatar
Posts: 161
Joined: Oct 06, 2006

SiriProxy plugin for Indigo

I set out today to create a plugin for SiriProxy that would control Indigo. No sense in letting the Crestron/HomeSeer/KNX/Homematic/X10 folks have all the fun: https://github.com/plamoni/SiriProxy/wiki/Plugins

EDIT: Update - All code is posted here: https://github.com/msreynolds/siriproxy-violet/

If you can get SiriProxy running, you should be able to paste the code below into siriproxy-example.rb, rebuild the gem, re-bundle, and run siriproxy.

Modify the line:
indigoresturl="http://yourcomputer.local:8176/"

This plugin uses a few siriproxy custom phrase listeners and curl to call the restful api of Indigo. You should be able to easily tailor this example to suit your own needs. If anyone wants to help clean this up, keep adding functionality, and post the updates here, that would be great!


Enjoy!
Matt

Code: Select all
######
# Mountain Labs, LLC
# mtnlabs.com
# Author: Matthew Reynolds
# matt at mtnlabs dot com
# SiriProxy plugin "Violet" for controlling your Indigo Home Automation server
######

require 'cora'
require 'siri_objects'
require 'pp'
require 'uri'


class SiriProxy::Plugin::Violet < SiriProxy::Plugin

  indigoresturl="http://yourcomputer.local:8176/"

  def initialize(config)
    #if you have custom configuration options, process them here!

  end

  #get the user's location and display it in the logs
  #filters are still in their early stages. Their interface may be modified
  filter "SetRequestOrigin", direction: :from_iphone do |object|
    puts "[Info - User Location] lat: #{object["properties"]["latitude"]}, long: #{object["properties"]["longitude"]}"

    #Note about returns from filters:
    # - Return false to stop the object from being forwarded
    # - Return a Hash to substitute or update the object
    # - Return nil (or anything not a Hash or false) to have the object forwarded (along with any
    #    modifications made to it)
  end

  listen_for /test siri proxy/i do
    # test greeting
    say "It's Siri Proxy in the house!"

    request_completed
  end

  listen_for /set thermostat to ([0-9]*[0-9])/i do |degrees|
    degrees.strip!
    ack = ask "You want me to set the thermostat to "+degrees+" degrees, correct?"
    if (ack =~ /yes/i or ack =~ /correct/i or ack =~ /that's right/i)
      begin
        url=indigoresturl+"devices/thermostat"
        curlrequest="curl -X PUT -d setpointHeat=#{degrees} "+url
        puts curlrequest
        system(curlrequest)
      rescue Exception=>e
        puts e.exception
        say "I was unable to complete your request"
      else
        say "Operation succeeded"
      ensure
        request_completed
      end
    else
      say "Operation aborted"
      request_completed
    end
  end

  listen_for /set brightness of ([a-z 0-9]*)/i do |devicename|
    devicename.strip!
    brightness = ask "To what value?"
    brightness.strip!
    ack = ask "You want me to set the brightness of "+devicename+" to "+brightness+", correct?"
    if (ack =~ /yes/i or ack =~ /correct/i or ack =~ /right/i or ack =~ /affirmative/i)
      begin
        devicename=URI::encode(devicename)
        brightness=URI::encode(brightness)
        url=indigoresturl+"devices/#{devicename}"
        curlrequest="curl -X PUT -d brightness=#{brightness} "+url
        puts curlrequest
        system(curlrequest)
      rescue Exception=>e
        puts e.exception
        say "I was unable to complete your request"
      else
        say "Operation succeeded"
      ensure
        request_completed
      end
    else
      say "Operation aborted"
      request_completed
    end
  end

  #indigo native functions
  listen_for /toggle ([a-z 1-9]*)/i do |devicename|
    devicename.strip!
    ack = ask "You want me to toggle "+devicename+", correct?"
    if (ack =~ /yes/i or ack =~ /correct/i or ack =~ /that's right/i)
      begin
        devicename=URI::encode(devicename)
        url=indigoresturl+"devices/#{devicename}"
        curlrequest="curl -X PUT -d toggle=1 "+url
        puts curlrequest
        system(curlrequest)
      rescue Exception=>e
        puts e.exception
        say "I was unable to complete your request"
      else
        say "Operation succeeded"
      ensure
        request_completed
      end
    else
      say "Operation aborted"
      request_completed
    end
  end

  listen_for /turn on ([a-z 1-9]*)/i do |devicename|
    devicename.strip!
    ack = ask "You want me to turn on "+devicename+", correct?"
    if (ack =~ /yes/i or ack =~ /correct/i or ack =~ /that's right/i)
      begin
        devicename=URI::encode(devicename)
        url=indigoresturl+"devices/#{devicename}"
        curlrequest="curl -X PUT -d isOn=1 "+url
        puts curlrequest
        system(curlrequest)
      rescue Exception=>e
        puts e.exception
        say "I was unable to complete your request"
      else
        say "Operation succeeded"
      ensure
        request_completed
      end
    else
      say "Operation aborted"
      request_completed
    end
  end

  listen_for /turn off ([a-z 0-9]*)/i do |devicename|
    devicename.strip!
    ack = ask "You want me to turn off "+devicename+", correct?"
    if (ack =~ /yes/i or ack =~ /correct/i or ack =~ /that's right/i)
      begin
        devicename=URI::encode(devicename)
        url=indigoresturl+"devices/#{devicename}"
        curlrequest="curl -X PUT -d isOn=0 "+url
        puts curlrequest
        system(curlrequest)
      rescue Exception=>e
        puts e.exception
        say "I was unable to complete your request"
      else
        say "Operation succeeded"
      ensure
        request_completed
      end
    else
      say "Operation aborted"
      request_completed
    end
  end

  listen_for /execute ([a-z 0-9]*)/i do |actionname|
    actionname.strip!
    ack = ask "You want me to execute the action group: "+actionname+", correct?"
    if (ack =~ /yes/i or ack =~ /correct/i or ack =~ /right/i)
      begin
        actionname=URI::encode(actionname)
        url=indigoresturl+"actions/#{actionname}"
        curlrequest="curl -X EXECUTE "+url
        puts curlrequest
        system(curlrequest)
      rescue Exception=>e
        puts e.exception
        say "I was unable to complete your request"
      else
        say "Operation succeeded"
      ensure
        request_completed
      end
    else
      say "Operation aborted"
      request_completed
    end
  end

end
Last edited by mreyn2005 on Tue Jan 29, 2013 6:42 pm, edited 1 time in total.

Posted on
Mon Oct 15, 2012 6:50 am
matt (support) offline
Site Admin
User avatar
Posts: 21421
Joined: Jan 27, 2003
Location: Texas

Re: SiriProxy plugin for Indigo

Cool, thanks for sharing it!

Sprinkler pause/resume (?activeZone=pause and ?activeZone=resume) might also be useful. I use those somewhat frequently to temporarily pause the sprinkler when I need to go into the yard (check the mail, etc.) or when someone is dropping something off at the house.

Image

Posted on
Wed Oct 24, 2012 2:14 pm
nsheldon offline
Posts: 2469
Joined: Aug 09, 2010
Location: CA

Re: SiriProxy plugin for Indigo

Hey! That's cool! I had no idea about SiriProxy until your post.

I got it running on my Mac mini (same one running Indigo 5.1.6) and have done quite a bit of tweaking. I created my own SiriProxy plugin to control Indigo, but it's using embedded AppleScripts (since SiriProxy is on the same box) instead of the RESTful method (it seems more flexible to use AppleScript). Thanks for sharing!

I can share the plugin code, but it's highly specialized to my specific Indigo setup. Probably the only things that anyone would get out of it might be the regular expressions used to match phrases to actions. If anyone's interested, I can share those.

Posted on
Wed Oct 24, 2012 6:28 pm
mreyn2005 offline
User avatar
Posts: 161
Joined: Oct 06, 2006

Re: SiriProxy plugin for Indigo

Hey N,
I am creating a cleaned up version of the original also. I will post it when I can get it where its better and still working ;-) I have not endeavored in ruby before this little project, so I know there are a lot of code improvements to be had.

Original Plan: Was to make a chatbot in python as a plugin for indigo and have it be the controller of indigo. The "Violet" plugin for SiriProxy would just be a passthrough to the indigo chat bot.

Current Implementation: SiriProxy already provides a nice interface for phrase dissection that would otherwise require some AIML magic and a middle tier in a good python chatbot plugin/framework to accomplish the same thing. So in the mean time, this is just the sample. I am otherwise not comfortable making system level calls for which there is/are public method interfaces (RESTful over HTTP) to do the same thing. One of my cleanup todo's is to use the native ruby classes for HTTP requests (I saw some examples of such in the Eliza plugin sample provided by the author of SiriProxy on his git repo.) that should be close to what I want to do.

Future plan: Make the chatbot plugin for indigo that has this control logic built into it. The plugin for Indigo can expose a RESTful API to our chatbot. It can use AIML for its vocabulary/input phrases/output phrases. The key would be the plugin would have an intercepting tier that could provide the response lookup from AIML and replace certain identifiers with value lookups from the python Indigo Object Model (exposed by Indigo for plugins). Use SiriProxy as a passthrough to make RESTful calls to our chatbot.

Props to your progress! I would be curious to see your stuff for sure!

Cheers,
Matt
Last edited by mreyn2005 on Mon Jan 28, 2013 11:58 am, edited 1 time in total.

Posted on
Thu Oct 25, 2012 1:24 am
nsheldon offline
Posts: 2469
Joined: Aug 09, 2010
Location: CA

Re: SiriProxy plugin for Indigo

Hey Matt.

Wow! You have some ambitious plans! It'd be great to see all that come to fruition. Yes, this is my first real exposure to Ruby as well. Before messing with SiriProxy, I'd never actually looked at any Ruby code (I'm still wrapping my head around Python). But, Google to the rescue. :-)

What I've got so far is little over 1300 lines of Ruby code, below.
Code: Select all
require 'cora'
require 'siri_objects'
require 'pp'

#######
# Indigo Control
#
#   version 0.5
#
#   by: Nathan Sheldon
#      nathan AT nathansheldon DOT com
#
# This SiriProxy plugin intercepts commands from Siri to control various
#   home automation functions through Perceptiove Automation's Indigo Pro
#   software running on the same host.
#
#   Version History:
#      0.5 (24-Oct-2012)
#         Initial release (internal only)
#
######

class SiriProxy::Plugin::IndigoControl < SiriProxy::Plugin
   def initialize(config)
      #if you have custom configuration options, process them here!
   end
   
   # Get the device's unique Siri ID so we know who's making the request.
   filter "LoadAssistant", direction: :from_iphone do |object|
      @assistantId = object["properties"]["assistantId"]
      puts "[Info - Assistant ID: #{@assistantId}"
      # Try to find an Indigo variable with that ID.
      result = `osascript <<EOF
tell application "IndigoServer"
   set deviceName to "none"
   set howMany to count (every variable whose value contains "#{@assistantId}")
   if howMany is 1 then
      set theVariable to the first variable whose value contains "#{@assistantId}"
      set variableValue to value of theVariable
      set AppleScript's text item delimiters to ","
      set deviceName to text item 1 of variableValue
      set AppleScript's text item delimiters to {""}
      set deviceName to deviceName as text
   end if
   return deviceName
end tell
EOF
`
      result = result.strip
      if result =~ /nPhone/i
         @siriUser = "Nathan"
      elsif result =~ /ePhone/i
         @siriUser = "Elizabeth"
      else
         @siriUser = "Guest"
      end
   end
   
   
   # --- ACTION METHODS ---
   #
   # MOTION ACTIVATION CONTROL METHOD
   #
   # Turn on or off motion activation.
   def motionActivationControl (onOff, area)
      spokenArea = area
      spokenArea = spokenArea.strip
      spokenArea = spokenArea.sub(/( on| off)/i, "")
      puts "#{onOff}, #{spokenArea}"
      area = area.strip
      onOff = onOff.strip
      # Standardize on/off commands.
      onOff = onOff.sub(/(enable|start|turn on)/i, "on")
      onOff = onOff.sub(/(disable|stop|turn off)/i, "off")
      # Extract on/off from area.
      if area =~ /on/i
         onOff = "on"
      elsif area =~ /off/i
         onOff = "off"
      end
      area = area.sub(/( on| off)/i, "")
      # Standardize commonly used names to match area naming convensions.
      area = area.sub("bedroom", "bed")
      area = area.sub("bed room", "bed")
      area = area.sub("bathroom", "bath")
      area = area.sub("bath room", "bath")
      area = area.sub(/hall /i, "hallway ")
      area = area.sub(/entry /i, "entryway ")
      area = area.sub("drive way", "driveway")
      area = area.sub("living room", "family room")
      area = area.sub(/guest.*/i, "guest bed")
      area = area.sub("front yard", "front porch")
      area = area.sub(/.*(house|home).*/i, "all")
      area = area.sub("everything", "all")
      area = area.sub("everywhere", "all")
      # Output logging info.
      time = Time.now
      puts "#{time}: [IndigoControl] Interpreted area: #{area}"
      puts "#{time}: [IndigoControl] Interpreted action: #{onOff}"
      # Use AppleScript to tell Indigo what to do.
      result = `osascript <<EOF
tell application "IndigoServer"
   set siriArea to "#{area}"
   set siriOnOff to "#{onOff}"
   -- Log that we're using Siri.
   if siriOnOff contains "enable" or siriOnOff contains "disable" then
      log "Siri: " & siriOnOff & " motion activation for #{spokenArea}..." using type "Siri Control"
   else
      log "Siri: turn " & siriOnOff & " motion activation for #{spokenArea}..." using type "Siri Control"
   end if
   
   if siriOnOff is "on" then
      if siriArea is "all" then
         set siriAction to "Enable All Automatic Lighting"
         set howMany to 1
      else
         set siriAction to "Enable Motion Activation - " & siriArea
         set howMany to count (every action group whose name is siriAction)
      end if
   else
      if siriArea is "all" then
         set siriAction to "Disable All Automatic Lighting"
         set howMany to 1
      else
         set siriAction to "Disable Motion Activation - " & siriArea
         set howMany to count (every action group whose name is siriAction)
      end if
   end if
   if howMany is not 1 then
      return howMany
   else
      set theActions to (every action group whose name is siriAction)
      repeat with thisAction in theActions
         set actionName to name of thisAction
         execute group actionName
      end repeat
   end if
   return -1
end tell
EOF
`
      result = Integer(result)
      if result == -1
         if !(area =~ /all/i)
            spokenArea = "the #{area}"
         end
         onOff = onOff.sub(/enable.?/i, "on")
         onOff = onOff.sub(/disable.?/i, "off")
         say "I've turnd #{onOff} motion activated lighting for #{spokenArea}."
      elsif result == 0
         say "Sorry, I don't recognize the #{spokenArea} area of the house."
      elsif result > 0
         say "I'm confused. There seems to be a few areas named #{spokenArea}. I don't know what to do."
      end
   end # End motion activation on/off.

   # DEVICE TOGGLE CONTROL METHOD
   #
   # Turn device on/off.
   def deviceOnOff(onOff, device)
      spokenDev = device
      spokenDev = spokenDev.strip
      device = device.strip
      # Standardize commonly used names to match device naming convensions.
      device = device.sub("lights", "light")
      device = device.sub("bedroom", "bed")
      device = device.sub("bed room", "bed")
      device = device.sub("bathroom", "bath")
      device = device.sub("bath room", "bath")
      device = device.sub("music light", "music room light")
      device = device.sub("guest light", "guest bed light")
      device = device.sub("living room", "family room")
      device = device.sub(/(family.*|living.*|ceiling.*) fan/i, "family room ceiling fan")
      device = device.sub(/hall.*/i, "hallway light")
      device = device.sub(/entry.*/i, "entryway light")
      device = device.sub(/guest.*/i, "guest bed light")
      device = device.sub(/porch.*/i, "front porch")
      device = device.sub("back light", "back yard motion light")
      device = device.sub(/back porch.*/i, "patio light")
      device = device.sub(/.*patio.*/i, "patio light")
      device = device.sub(/back yard.*/i, "back yard motion light")
      device = device.sub(/drive.*/i, "driveway motion light")
      device = device.sub(/front yard.*/i, "driveway motion light")
      device = device.sub("night stand", "nightstand")
      device = device.sub("Sensi", "scentsy")
      device = device.sub("Cincy", "scentsy")
      if device =~ /^my .*(lamp.?|light.?)/i
         if @siriUser =~ /elizabeth/i
            device = "Elizabeth's Lamp"
         elsif @siriUser =~ /nathan/i
            device = "Nathan's Lamp"
         end
      end
      # Put the device name into title case to match naming convention used in Indigo.
      device = device.split(" ").map {|word| word.capitalize}.join(" ")
      # Output logging info.
      time = Time.now
      puts "#{time}: [IndigoControl] Interpreted device: #{device}"
      puts "#{time}: [IndigoControl] Interpreted action: #{onOff}"
      # Control the device with AppleScript.
      result = `osascript <<EOF
tell application "IndigoServer"
   set spokenDevice to "#{spokenDev}"
   set siriAction to "#{onOff}"
   set siriDevice to "#{device}"
   set variableName to siriDevice
   -- Interpret room lighting group names.
   if siriDevice contains "family room" and (siriDevice contains "light" or siriDevice contains "lamp") then
      set siriDevice to "Family Room West Lamp"
      set variableName to "FamilyRoomLight"
   else if siriDevice contains "Elizabeth" or siriDevice contains "Nathan" or siriDevice contains "both" or (siriDevice contains "my" and (siriDevice contains "light" or siriDevice contains "lamp")) then
      set variableName to ""
      set bothLamps to false
      if siriDevice contains "both" or siriDevice contains "lamps" then
         set siriDevice to "Master Bed Nightstand Lamp 1"
         set bothLamps to true
      else if siriDevice contains "Elizabeth" then
         set siriDevice to "Master Bed Nightstand Lamp 1"
      else if siriDevice contains "Nathan" then
         set siriDevice to "Master Bed Nightstand Lamp 2"
      end if
   else if siriDevice contains "bed" then
      if "#{@siriUser}" is "Elizabeth" or "#{@siriUser}" is "Nathan" then
         set siriDevice to "Master Bed Light"
      end if
   end if
   -- Log that we're using Siri.
   log "Siri: turn " & siriAction & " " & spokenDevice & "..." using type "Siri Control"
   -- Convert device name to a variable name for some lighting controls.
   set useVariable to false
   if variableName is not "" then
      set AppleScript's text item delimiters to " "
      set variableName to text items of variableName
      set AppleScript's text item delimiters to {""}
      set variableName to (variableName as text)
      if variableName does not contain "light" and variableName does not contain "lamp" then
         set variableName to variableName & "Light"
      end if
      set rateVariableName to variableName & "RampRate"
      set levelVariableName to variableName & "Brightness"
      if (variable rateVariableName exists) and (variable levelVariableName exists) then
         set useVariable to true
      end if
   end if
   
   -- Control the device based on whether we're a variable to control it or not.
   if useVariable is true then
      if value of variable rateVariableName is not "" then
         if (value of variable rateVariableName) as number > 2 then
            set value of variable rateVariableName to "2"
         end if
      else
         set value of variable rateVariableName to "2"
      end if
      if siriAction is "on" then
         set value of variable levelVariableName to "100"
      else
         set value of variable levelVariableName to "0"
      end if
   else
      set howMany to count (every device whose (name is siriDevice or name is siriDevice & " Light" or name is siriDevice & " Lamp"))
      if howMany is not 1 then
         return howMany
      else
         set theDevices to (every device whose (name is siriDevice or name is siriDevice & " Light" or name is siriDevice & " Lamp"))
         repeat with thisDevice in theDevices
            set deviceName to name of thisDevice
            if supports on off of thisDevice is true then
               if deviceName is "Master Bed Nightstand Lamp 1" then
                  if bothLamps is true then
                     turn #{onOff} thisDevice
                     if value of variable "isMasterBedNightstandSync" is "false" then
                        turn #{onOff} device "Master Bed Nightstand Lamp 2"
                     end if
                  else
                     if value of variable "isMasterBedNightstandSync" is "true" then
                        -- Disable nightstand brightness sync.
                        set value of variable "isMasterBedNightstandSync" to "false"
                     end if
                     turn #{onOff} thisDevice
                  end
               else
                  turn #{onOff} thisDevice
               end if
            else
               return -2
            end if
         end repeat
      end if
   end if
   return -1
end tell
EOF
`
      result = Integer(result)
      if result == -1
         if device =~ /family room (light.?|lamp.?)/i
            say "I've turnd #{onOff} the family room lights."
         elsif device =~ /(nathan|elizabeth|both).*(light.?|lamp.?)/i
            say "I've turnd #{onOff} #{device}."
         else
            say "I've turned #{onOff} the #{device}."
         end
      elsif result == -2
         say "Sorry, I can't turn #{onOff} that device."
      elsif result == 0
         if spokenDev =~ /my/i
            say "Sorry, I don't recognize you. You'll have to tell exactly which device to turn #{onOff}."
         else
            say "Sorry, I can't find a device named #{spokenDev} to turn #{onOff}."
         end
      elsif result > 0
         say "I'm confused. There seems to be a few things named #{spokenDev}. I don't know what to do."
      end
   end # End device on/off control.

   # DEVICE BRIGHTNESS METHOD
   #
   # Set device to brightness amount
   def deviceBrightnessControl(relative, upDown, device, brightness)
      spokenDev = device
      spokenDev = spokenDev.strip
      device = device.strip
      # Standardize commonly used names to match device naming convensions.
      device = device.sub("lights", "light")
      device = device.sub("bedroom", "bed")
      device = device.sub("bed room", "bed")
      device = device.sub("bathroom", "bath")
      device = device.sub("bath room", "bath")
      device = device.sub("music light", "music room light")
      device = device.sub("living room", "family room")
      device = device.sub(/(family.*|living.*|ceiling.*) fan/i, "family room ceiling fan")
      device = device.sub(/hall.*/i, "hallway light")
      device = device.sub(/entry.*/i, "entryway light")
      device = device.sub(/guest.*/i, "guest bed light")
      device = device.sub(/porch.*/i, "front porch")
      device = device.sub("back light", "back yard motion light")
      device = device.sub(/back porch.*/i, "patio light")
      device = device.sub(/.*patio.*/i, "patio light")
      device = device.sub(/back yard.*/i, "back yard motion light")
      device = device.sub(/drive.*/i, "driveway motion light")
      device = device.sub(/front yard.*/i, "driveway motion light")
      device = device.sub("night stand", "nightstand")
      device = device.sub("Sensi", "scentsy")
      device = device.sub("Cincy", "scentsy")
      if device =~ /^my .*(lamp.?|light.?)/i
         if siriUser =~ /elizabeth/i
            device = "Elizabeth's Lamp"
         elsif siriUser =~ /nathan/i
            device = "Nathan's Lamp"
         end
      end
      # Put the device name into title case to match naming convention used in Indigo.
      device = device.split(" ").map {|word| word.capitalize}.join(" ")
      # Standardize brightness numbers.
      brightness = brightness.sub("zero", "0")
      brightness = brightness.sub("one", "1")
      brightness = brightness.sub("two", "2")
      brightness = brightness.sub("three", "3")
      brightness = brightness.sub("four", "4")
      brightness = brightness.sub("five", "5")
      brightness = brightness.sub("six", "6")
      brightness = brightness.sub("seven", "7")
      brightness = brightness.sub("eight", "8")
      brightness = brightness.sub("nine", "9")
      brightness = brightness.sub(/ off.*/i, "0")
      if upDown =~ /.*down.*/i
         brightness = brightness.sub(/full.*|complet.*|all.*/i, "0")
      else
         brightness = brightness.sub(/full.*|complet.*|all.*/i, "100")
      end
      # Output logging info.
      time = Time.now
      puts "#{time}: [IndigoControl] Interpreted device: #{device}"
      puts "#{time}: [IndigoControl] Interpreted brightness: #{upDown} #{relative} #{brightness}"
      # Control the device with AppleScript.
      result = `osascript <<EOF
tell application "IndigoServer"
   set spokenDevice to "#{spokenDev}"
   set siriAction to "#{brightness}"
   set siriRelative to "#{relative}"
   set siriUpDown to "#{upDown}"
   set siriDevice to "#{device}"
   set variableName to siriDevice
   -- Interpret room lighting group names.
   if siriDevice contains "family room" and (siriDevice contains "light" or siriDevice contains "lamp") then
      set siriDevice to "Family Room West Lamp"
      set variableName to "FamilyRoomLight"
   else if siriDevice contains "Elizabeth" or siriDevice contains "Nathan" or siriDevice contains "both" or (siriDevice contains "my" and (siriDevice contains "light" or siriDevice contains "lamp")) then
      set variableName to ""
      set bothLamps to false
      if siriDevice contains "both" or siriDevice contains "lamps" then
         set siriDevice to "Master Bed Nightstand Lamp 1"
         set bothLamps to true
      else if siriDevice contains "Elizabeth" then
         set siriDevice to "Master Bed Nightstand Lamp 1"
      else if siriDevice contains "Nathan" then
         set siriDevice to "Master Bed Nightstand Lamp 2"
      end if
   else if siriDevice contains "bed" then
      if "#{@siriUser}" is "Elizabeth" or "#{@siriUser}" is "Nathan" then
         set siriDevice to "Master Bed Light"
      end if
   end if
   -- Log that we're using Siri.
   log "Siri: brighten " & spokenDevice & " " & siriUpDown & " " & siriRelative & " " & siriAction & "..." using type "Siri Control"
   -- Convert device name to a variable name for some lighting controls.
   set useVariable to false
   if variableName is not "" then
      set AppleScript's text item delimiters to " "
      set variableName to text items of variableName
      set AppleScript's text item delimiters to {""}
      set variableName to (variableName as text)
      if variableName does not contain "light" and variableName does not contain "lamp" then
         set variableName to variableName & "Light"
      end if
      set rateVariableName to variableName & "RampRate"
      set levelVariableName to variableName & "Brightness"
      if (variable rateVariableName exists) and (variable levelVariableName exists) then
         set useVariable to true
      end if
   end if
   
   -- Control the device based on whether we're a variable to control it or not.
   if useVariable is true then
      if value of variable rateVariableName is not "" then
         if (value of variable rateVariableName) as number > 2 then
            set value of variable rateVariableName to "2"
         end if
      else
         set value of variable rateVariableName to "2"
      end if
      if siriDevice does not contain "light" then
         set currentBrightness to brightness of device (siriDevice & " Light")
      else
         set currentBrightness to brightness of device siriDevice
      end if
      if siriRelative is "by" then
         if siriUpDown is "up" then
            set newBrightness to currentBrightness + (siriAction as number)
         else if siriUpDown is "down" then
            set newBrightness to currentBrightness - (siriAction as number)
         end if
      else if siriRelative is "to" then
         set newBrightness to (siriAction) as number
      end if
      if newBrightness < 0 then
         set newBrightness to 0
      else if newBrightness > 100 then
         set newBrightness to 100
      end if
      set value of variable levelVariableName to (newBrightness as text)
   else
      set howMany to count (every device whose (name is siriDevice or name is siriDevice & " Light" or name is siriDevice & " Lamp"))
      if howMany is not 1 then
         return howMany
      else
         set theDevices to (every device whose (name is siriDevice or name is siriDevice & " Light" or name is siriDevice & " Lamp"))
         repeat with thisDevice in theDevices
            set deviceName to name of thisDevice
            set currentBrightness to brightness of thisDevice
            if siriRelative is "by" then
               if siriUpDown is "up" then
                  set newBrightness to currentBrightness + (siriAction as number)
               else if siriUpDown is "down" then
                  set newBrightness to currentBrightness - (siriAction as number)
               end if
            else if siriRelative is "to" then
               set newBrightness to (siriAction) as number
            end if
            if newBrightness < 0 then
               set newBrightness to 0
            else if newBrightness > 100 then
               set newBrightness to 100
            end if
            if supports dimming of thisDevice is true then
               if deviceName is "Master Bed Nightstand Lamp 1" and bothLamps is true then
                  set brightness of thisDevice to newBrightness
                  if value of variable "isMasterBedNightstandSync" is "false" then
                     set brightness of device "Master Bed Nightstand Lamp 2" to newBrightness
                  end if
               else
                  if value of variable "isMasterBedNightstandSync" is "true" then
                     -- Disable nightstand lamp sync.
                     set value of variable "isMasterBedNightstandSync" to "false"
                  end if
                  set brightness of thisDevice to newBrightness
               end if
            else
               return -2
            end if
         end repeat
      end if
   end if
   return -1
end tell
EOF
`
      result = Integer(result)
      if result == -1
         if device =~ /family room (light.?|lamp.?)/i
            if relative =~ /by/i
               if upDown =~ /up/i
                  upDown = "increased"
               else
                  upDown = "decreased"
               end
               say "I've #{upDown} the brightness of the family room lights by #{brightness} percent."
            else
               say "I've set the brightness of the family room lights to #{brightness} percent."
            end
         elsif device =~ /fan/i
            if relative =~ /by/i
               if upDown =~ /up/i
                  upDown = "increased"
               else
                  upDown = "decreased"
               end
               say "I've #{upDown} the speed of the #{device} by #{brightness} percent."
            else
               say "I've set the speed of the #{device} to #{brightness} percent."
            end
         elsif device =~ /(nathan|elizabeth|both).*(light.?|lamp.?)/i
            if relative =~ /by/i
               if upDown =~ /up/i
                  upDown = "increased"
               else
                  upDown = "decreased"
               end
               say "I've #{upDown} the brightness of #{device} by #{brightness} percent."
            else
               say "I've set the brightness of #{device} to #{brightness} percent."
            end
         else
            if relative =~ /by/i
               if upDown =~ /up/i
                  upDown = "increased"
               else
                  upDown = "decreased"
               end
               say "I've #{upDown} the #{device} brightness by #{brightness} percent."
            else
               say "I've set the #{device} brightness to #{brightness} percent."
            end
         end
      elsif result == -2
         say "Sorry, I can only turn that device on or off."
         response = ask "Would you like me to turn on the #{device}?"
         if (response =~ /yea|yes|okay|i guess|sure|affirmative|absolutely|of course/i)
            # Output logging info.
            time = Time.now
            puts "#{time}: [IndigoControl] Interpreted device: #{device}"
            puts "#{time}: [IndigoControl] Interpreted action: On"
            result = `osascript <<EOF
tell application "IndigoServer"
   set spokenDevice to "#{spokenDev}"
   set siriAction to "#{brightness}"
   set siriDevice to "#{device}"
   set howMany to count (every device whose (name is siriDevice or name is siriDevice & " Light" or name is siriDevice & " Lamp"))
   if howMany is not 1 then
      return howMany
   else
      set theDevices to (every device whose (name is siriDevice or name is siriDevice & " Light" or name is siriDevice & " Lamp"))
      repeat with thisDevice in theDevices
         set deviceName to name of thisDevice
         if supports on off of thisDevice is true then
            turn on thisDevice
         else
            return -2
         end if
      end repeat
   end if
   return -1
end tell
EOF
`
            result = Integer(result)
            if result == -1
               say "I've turned on the #{device}."
            elsif result == -2
               say "Sorry, I can't turn on that device."
            elsif result == 0
               if spokenDev =~ /my/i
                  say "Sorry, I don't recognize you. You'll have to tell exactly which device to turn on."
               else
                  say "Sorry, I can't find a device named #{spokenDev} to turn on."
               end
            elsif result > 0
               say "I'm confused. There seems to be a few things named #{spokenDev}. I don't know what to do."
            end
         else
            say "Okay. I won't turn it on."
         end
      elsif result == 0
         if spokenDev =~ /my/i
            say "Sorry, I don't recognize you. You'll have to tell exactly which device to turn adjust."
         else
            say "Sorry, I can't find a device named #{spokenDev}."
         end
      elsif result > 0
         say "I'm confused. There seems to be a few things named #{spokenDev}. I don't know what to do."
      end
   end # End device brightness control.

   # DOOR CONTROL METHODS
   #
   # Garage door open/close.
   def garageDoorControl(openClose, device)
      # Output logging info.
      time = Time.now
      puts "#{time}: [IndigoControl] Interpreted device: #{device}"
      puts "#{time}: [IndigoControl] Interpreted action: #{openClose} - Request"
      # If OPEN or CLOSE...
      if openClose =~ /open/i
         # If OPEN...
         result = `osascript <<EOF
tell application "IndigoServer"
   set p to binary inputs of device "Garage Door"
   if item 1 of p is true then
      -- Garage door is closed.
      return 1
   else
      -- Garage door is open.
      return 0
   end if
   return -1
end tell
EOF
`
         result = Integer(result)
         if result == 0
            say "The garage door is already open."
         elsif result == 1
            response = ask "Are you sure you want to open the garage door?"
            if (response =~ /yea|yes|^okay|i guess|sure|affirmative|absolutely|of course/i)
               # Output logging info.
               time = Time.now
               puts "#{time}: [IndigoControl] Interpreted device: #{device}"
               puts "#{time}: [IndigoControl] Interpreted action: #{openClose} - Confirm"
               result = `osascript <<EOF
tell application "IndigoServer"
   set p to binary inputs of device "Garage Door"
   if item 1 of p is true then
      -- Garage door is closed.
      execute group "Open/Close Garage Door"
      return 1
   else
      -- Garage door is open.
      return 0
   end if
   return -1
end tell
EOF
`
               result = Integer(result)
               if result == 0
                  say "The garage door is already open."
               elsif result == 1
                  say "I've opened the garage door."
               elsif result == -1
                  say "Sorry, there was a problem finding the Garage Door in the home automation system."
               end
            else
               say "Okay. I won't open the garage door."
            end
         elsif result == -1
            say "Sorry, there was a problem finding the Garage Door in the home automation system."
         end
      elsif openClose =~ /close/i
         # If CLOSE...
         result = `osascript <<EOF
tell application "IndigoServer"
   set p to binary inputs of device "Garage Door"
   if item 1 of p is true then
      -- Garage door is closed.
      return 1
   else
      -- Garage door is open.
      return 0
   end if
   return -1
end tell
EOF
`
         result = Integer(result)
         if result == 1
            say "The garage door is already closed."
         elsif result == 0
            response = ask "Are you sure you want to close the garage door?"
            if (response =~ /yea|yes|^okay|i guess|sure|affirmative|absolutely|of course/i)
               # Output logging info.
               time = Time.now
               puts "#{time}: [IndigoControl] Interpreted device: Garage Door"
               puts "#{time}: [IndigoControl] Interpreted action: Close - Confirm"
               result = `osascript <<EOF
tell application "IndigoServer"
   set p to binary inputs of device "Garage Door"
   if item 1 of p is true then
      -- Garage door is closed.
      return 1
   else
      -- Garage door is open.
      execute group "Open/Close Garage Door"
      return 0
   end if
   return -1
end tell
EOF
`
               result = Integer(result)
               if result == 0
                  say "I've closed the garage door."
               elsif result == 1
                  say "The garage door is already closed."
               elsif result == -1
                  say "Sorry, there was a problem finding the Garage Door in the home automation system."
               end
            else
               say "Okay. I won't close the garage door."
            end
         elsif result == -1
            say "Sorry, there was a problem finding the Garage Door in the home automation system."
         end
      end # If open or close.
   end # End garage door control

   # Front door lock or unlock
   def frontDoorControl(lockUnlock, device)
      # Output logging info.
      time = Time.now
      puts "#{time}: [IndigoControl] Interpreted device: #{device}"
      puts "#{time}: [IndigoControl] Interpreted action: #{lockUnlock}"
      # Lock or Unlock...
      if lockUnlock =~ /^unlock/i
         # UNLOCK...
         result = `osascript <<EOF
tell application "IndigoServer"
   set lockState to on state of device "Front Door Lock"
   turn off device "Front Door Lock"
   if lockState is true then
      return 0
   else if lockState is false then
      return 1
   end if
end tell
EOF
`
         result = Integer(result)
         if result == 0
            say "I've unlocked the Front Door."
         elsif result == 1
            say "I've unlocked the Front Door, though it appeared to already be unlocked."
         end
      elsif lockUnlock =~ /^lock/i
         # LOCK...
         result = `osascript <<EOF
tell application "IndigoServer"
   set lockState to on state of device "Front Door Lock"
   turn on device "Front Door Lock"
   if lockState is true then
      return 0
   else if lockState is false then
      return 1
   end if
end tell
EOF
`
         result = Integer(result)
         if result == 0
            say "I've locked the Front Door, though it appeared to already be locked."
         elsif result == 1
            say "I've locked the Front Door."
         end
      end # End if unlocking or locking.
   end # End front door control.

   # HOME THEATER CONTROL METHODS
   #
   # Watch Netflix
   def homeTheaterControl(command)
      # Log the action.
      time = Time.now
      puts "#{time}: [IndigoControl] Interpreted Home Theater Command: #{command}"
      # What command?
      if command =~ /netflix/i
         # NETFLIX...
         location = ""
         if @siriUser =~ /elizabeth|nathan/i
            result = `osascript <<EOF
tell application "IndigoServer"
   set FamilyRoomTV to value of variable "isWatchingTV"
   set MasterBedTV to value of variable "isMasterBedWatchingTV"
   if FamilyRoomTV is "false" then
      if MasterBedTV is "false" then
         return "choose"
      else
         return "Family Room"
      end if
   else
      if MasterBedTV is "false" then
         return "Master Bed"
      else
         return "choose"
      end if
   end if
end tell
EOF
`
            result = result.strip
            if result =~ /choose/i
               response = ask "Sounds good. In your room or in the family room?"
               if (response =~ /bed|my|our/i)
                  location = "Master Bed"
                  say "Okay. I'm setting up the Blu-ray player. I'm afraid you'll have to turn on the TV and receiver with the remote controls though."
               elsif (response =~ /living|family/i)
                  location = "Family Room"
                  say "Okay. I'm setting up Netflix for you."
               elsif (response =~ /here/i)
                  say "Actually, I can't tell exactly where you are in the hosue. You'll have to tell me where you're at."
               end
            else
               location = result
               say "Sounds good. I'm setting up Netflix for you in the #{location}."
            end
         else
            location = "Family Room"
            say "Sounds good. I'm setting up Netflix for you."
         end
         if location != ""
            result = `osascript <<EOF
tell application "IndigoServer"
   execute group "Macro - #{location} - Netflix on Blu-ray"
end tell
EOF
`
         end
      elsif command =~ /movie/i
         # MOVIE/BLU-RAY/DVD...
         location = ""
         if @siriUser =~ /elizabeth|nathan/i
            result = `osascript <<EOF
tell application "IndigoServer"
   set FamilyRoomTV to value of variable "isWatchingTV"
   set MasterBedTV to value of variable "isMasterBedWatchingTV"
   if FamilyRoomTV is "false" then
      if MasterBedTV is "false" then
         return "choose"
      else
         return "Family Room"
      end if
   else
      if MasterBedTV is "false" then
         return "Master Bed"
      else
         return "choose"
      end if
   end if
end tell
EOF
`
            result = result.strip
            if result =~ /choose/i
               response = ask "Sounds good. In your room or in the family room?"
               if (response =~ /bed|my|our/i)
                  location = "Master Bed"
                  say "Okay. I'm setting up the Blu-ray player. I'm afraid you'll have to turn on the TV and receiver with the remote controls though."
               elsif (response =~ /living|family/i)
                  location = "Family Room"
                  say "Okay. I'm setting everything up for you to watch a movie."
               elsif (response =~ /here/i)
                  say "Actually, I can't tell exactly where you are in the hosue. You'll have to tell me where you're at."
               end
            else
               location = result
               say "Sounds good. I'm setting everything up for you to watch a movie in the #{location}."
            end
         else
            location = "Family Room"
            say "Sounds good. I'm setting everything up for you to watch a movie."
         end
         if location != ""
            result = `osascript <<EOF
tell application "IndigoServer"
   execute group "Macro - #{location} - Play Blu-ray/DVD/CD"
end tell
EOF
`
         end
      elsif command =~ /computer/i
         # COMPUTER/MEDIA MAC
         say "Okay. I'm setting everything up for you to use the Media Mac in the Family Room."
         result = `osascript <<EOF
tell application "IndigoServer"
   execute group "Macro - Family Room - Use Media Mac"
end tell
EOF
`
      elsif command =~ /all off/i
         # ALL OFF
         location = ""
         if @siriUser =~ /elizabeth|nathan/i
            result = `osascript <<EOF
tell application "IndigoServer"
   set FamilyRoomTV to value of variable "isWatchingTV"
   set MasterBedTV to value of variable "isMasterBedWatchingTV"
   if FamilyRoomTV is "true" then
      if MasterBedTV is "true" then
         return "choose"
      else
         return "Family Room"
      end if
   else
      if MasterBedTV is "true" then
         return "Master Bed"
      else
         return "choose"
      end if
   end if
end tell
EOF
`
            result = result.strip
            if result =~ /choose/i
               response = ask "Okay. Turn things off in your room or the family room?"
               if (response =~ /bed|my|our/i)
                  location = "Master Bed"
                  say "No problem. I'm turning off the Blu-ray player. I'm afraid you'll have to use the remote controls to turn off the TV and receiver."
               elsif (response =~ /living|family/i)
                  location = "Family Room"
                  say "I've turned things off in the family room."
               elsif (response =~ /here/i)
                  say "Actually, I can't tell exactly where you are in the hosue. You'll have to tell me where you're at."
               end
            else
               location = result
               say "Okay. I've turned things off in the #{location}."
            end
         else
            loation = "Family Room"
            say "Okay. I've turned things off for you."
         end
         if location != ""
            result = `osascript <<EOF
tell application "IndigoServer"
   execute group "Macro - #{location} - All Off"
end tell
EOF
`
         end
      end # End Home Theater command type
   end # End home theater control

   # SPOKEN ANNOUNCEMENT METHODS
   #
   # Say Something
   def saySomething()
      response1 = ask "Okay. What would you like to say to everyone in the house?"
      response2 = ask "Announce this? \"" + response1 + "\"", spoken: "Got it. Is this what you'd like to announce?"
      if (response2 =~ /^yea|^yes|^sure|look.*ok.?.?|good enough|i guess/i)
         result = `osascript <<EOF
tell application "IndigoServer"
   set value of variable "Say_This" to "#{response1}"
   return "done"
end tell
EOF
`
         if (result =~ /done/)
            say "Announcement coming up."
         else
            say "Oops. Looks like there was a home automation server error."
         end
      else
         say "Very well. Announcement canceled."
      end
   end # End say something

   # HVAC AND TEMPERATURE METHODS
   #
   # Increase the Indoor Temperature
   def increaseIndoorTemp(setPointInt)
      setPointStr = setPointInt.to_s
      result = `osascript <<EOF
tell application "IndigoServer"
   set goal to ("#{setPointStr}") as number
   set currentCool to cool setpoint of device "Main Thermostat"
   set currentHeat to heat setpoint of device "Main Thermostat"
   if currentCool < goal then
      -- There needs to be at leat 4 degrees between cool and heat setpoints.
      if goal - 4 > currentHeat then
         set heat setpoint of device "Main Thermostat" to (goal - 4)
      end if
      set cool setpoint of device "Main Thermostat" to goal
   else if currentHeat < goal then
      -- There needs to be at leat 4 degrees between cool and heat setpoints.
      if goal + 4 > currentCool then
         set cool setpoint of device "Main Thermostat" to (goal + 4)
      end if
      set heat setpoint of device "Main Thermostat" to goal
   end if
   return "done"
end tell
EOF
`
      if result =~ /done/i
         return 0
      else
         return 1
      end
   end # End increase indoor temperature
   
   # Decrease the Indoor Temperature
   def decreaseIndoorTemp(setPointInt)
      setPointStr = setPointInt.to_s
      result = `osascript <<EOF
tell application "IndigoServer"
   set goal to ("#{setPointStr}") as number
   set currentCool to cool setpoint of device "Main Thermostat"
   set currentHeat to heat setpoint of device "Main Thermostat"
   if currentCool > goal then
      -- There needs to be at leat 4 degrees between cool and heat setpoints.
      if goal - 4 > currentHeat then
         set heat setpoint of device "Main Thermostat" to (goal - 4)
      end if
      set cool setpoint of device "Main Thermostat" to goal
   else if currentHeat > goal then
      -- There needs to be at leat 4 degrees between cool and heat setpoints.
      if goal + 4 > currentCool then
         set cool setpoint of device "Main Thermostat" to (goal + 4)
      end if
      set heat setpoint of device "Main Thermostat" to goal
   end if
   return "done"
end tell
EOF
`
      if result =~ /done/i
         return 0
      else
         return 1
      end
   end # End decrease indoor temperature
   
   # Current Indoor Temperature
   def currentIndoorTemp()
      result = `osascript <<EOF
tell application "IndigoServer"
   set theTemperature to (temperatures of device "Main Thermostat") as number
   return theTemperature
end tell
EOF
`
      theTemp = result.strip
      theTempInteger = result.to_i
      say "It's currently " + theTempInteger.to_s + " degrees indoors."
   end
   
   # --- END ACTION METHODS ---



   # --- SIRI PHRASE MATCHING ---
   #
   # MOTION ACTIVATION CONTROL
   #
   listen_for /(enable|disable|turn on|turn off|stop|start) (?:automatic |motion ).* (?:in |for )(?:the )?(.+)/i do |onOff, area|
      
      motionActivationControl(onOff, area)
      
   end

   # Keep the lights on/off in the area (on/off).
   listen_for /keep(?: the)? (.+ light.?) .*(on|off)/i do |area|
      
      motionActivationControl("off", area)
      
   end
   
   # DEVICE ON/OFF
   #
   # Turn on/off a device or a room's lights.
   listen_for /(?:turn|switch) (on|off)(?: the)? (.+)/i do |onOff, device|
      
      deviceOnOff(onOff, device)
      
   end

   # Turn a device or room's lights On/Off.
   listen_for /(?:turn|switch)(?: the)? (.+) (on|off)/i do |device, onOff|
      
      deviceOnOff(onOff, device)
      
   end
   
   # DEVICE BRIGHTNESS
   #
   # Set device to brightness amount
   listen_for /(set|dim|decrease|bright.n|increase|adjust|bring|turn)( down| up)?(?: the)?(?: bright.+ (?:of|in) the| speed of the)? (.+) ([0-9]+|zero|one|two|three|four|five|six|seven|eight|nine|full|off|complete.+|all the way)(?: brightness| speed)?/i do |brightDim, upDown, device, brightness|
      # Going up or down in brightness?
      if (device =~ / up/i) or (upDown =~ /.*up.*/i) or (brightDim =~ /.*bright.*|.*increase.*/i)
         upDown = "up"
      elsif (device =~ / down.*/i) or (upDown =~ /.*down.*/i) or (brightDim =~ /.*dim.*|.*decrease.*/i)
         upDown = "down"
      else
         upDown = ""
      end
      # Determine if we're using relative or absolute brightness numbers.
      if device =~ / by/i
         relative = "by"
      else
         relative = "to"
      end
      # Remove extra words from the device name.
      device = device.sub(/( bright.*| speed| up| down| to)/i, "")
      device = device.sub(/( bright.*| speed| up| down| to)/i, "")
      device = device.sub(/ brightness| illumination| speed/i, "")
      device = device.sub(/ to| by/i, "")
      # Remove extra words from the brightness.
      brightness = brightness.sub(/.*percent/i, "")
      
      deviceBrightnessControl(relative, upDown, device, brightness)
      
   end
   
   # DOOR CONTROL
   #
   # Garage Door
   #   Open
   listen_for /open(?: the)? garage door/i do
      
      garageDoorControl("open", "Garage Door")
      
   end
   
   # Garage Door
   #   Close
   listen_for /(?:close|shut)(?: the)? garage door/i do
      
      garageDoorControl("close", "Garage Door")
      
   end
   
   # Front Door
   #   Unlock
   listen_for /(unlock|unsecure|disengage)(?: the)? front door(?: lock)?/i do
      
      frontDoorControl("unlock", "Front Door Lock")
      
   end
   
   # Front Door
   #   Lock
   listen_for /^(?:lock|secure|engage)(?: the)? front door(?: lock)?/i do
      
      frontDoorControl("lock", "Front Door Lock")
      
   end
   
   # HOME THEATER CONTROL
   #
   # Watch Netflix
   listen_for /(watch|see|use|check out).*netflix.*/i do
      
      homeTheaterControl("netflix")
      
   end # End Netflix macro.
   
   # Watch a Blu-ray
   listen_for /(watch|see|use|check out).*blu.?ray|dvd|movie.*/i do
      
      homeTheaterControl("movie")
      
   end # End Blu-ray/movie macro.
   
   # Use the Media Mac
   listen_for /use.* (mac|computer)/i do
      
      homeTheaterControl("computer")
      
   end # End Use Computer macro.
   
   # Entertainment Center All Off
   listen_for /(done|finished|through|stop).*watch.*/i do
      
      homeTheaterControl("all off")
      
   end # End Entertainment Center All Off macro.
   
   # SPOKEN ANNOUNCEMENTS
   #
   # Say Something
   listen_for /.*(say|announce|tell|make).*(something|message|announce).*/i do
      
      saySomething()
      
   end # Say Something
   
   # INCREASE INDOOR TEMPERATURE (I'M COLD)
   listen_for /.*(set|adjust|make|bring|drop|decrease( the)?.*temperature)|((i.?.?m)|(it.?.?).*cold).*/i do
      
      result = `osascript <<EOF
tell application "IndigoServer"
   set theTemperature to (temperatures of device "Main Thermostat") as number
   return theTemperature
end tell
EOF
`
      theTemp = result.to_i
      continue = false
      response = ask "I can help with that. It's currently " + theTemp.to_s + " inside. What would be a more comfortable temperature?"
      while continue == false do
         if response =~ /([0-9]+|zero|one|two|three|four|five|six|seven|eight|nine)/i
            response = $1
            # Standardize brightness numbers.
            response = response.sub("zero", "0")
            response = response.sub("one", "1")
            response = response.sub("two", "2")
            response = response.sub("three", "3")
            response = response.sub("four", "4")
            response = response.sub("five", "5")
            response = response.sub("six", "6")
            response = response.sub("seven", "7")
            response = response.sub("eight", "8")
            response = response.sub("nine", "9")
            if response.to_i < 50
               say "I thought you said you were cold! The thermostat can't even go below 50 degrees anyway."
            elsif response.to_i > 99
               say "Actually, the thermostat can't go that high."
            elsif response.to_i < theTemp
               say "I thought you said you were cold. Anyway..."
               
               decreaseIndoorTemp(response.to_i)
               say "I've set the thermostat to drop the temperature to " + response + " degrees."
               continue = true
            else
               
               increaseIndoorTemp(response.to_i)
               say "Okay. I've set the thermostat to bring the temperature to " + response + " degrees."
               continue = true
            end
         elsif response =~ /never|forget|stop|cancel|go away/i
            say "Okay. I won't change any thermostat settings."
            continue = true
         else
            say "Actually, I was hoping for a number."
         end
         if continue == false
            response = ask "What would be a comfortable temperature?"
         end
      end # While loop.
   end # End increase indoor temperature.
   
   # DECREASE INDOOR TEMPERATURE (I'M HOT)
   listen_for /.*(set|adjust|make|bring|drop|decrease( the)?.*temperature)|((i.?.?m)|(it.?.?).*hot).*/i do
      
      result = `osascript <<EOF
tell application "IndigoServer"
   set theTemperature to (temperatures of device "Main Thermostat") as number
   return theTemperature
end tell
EOF
`
      theTemp = result.to_i
      continue = false
      response = ask "I can help with that. It's currently " + theTemp.to_s + " inside. What would be a more comfortable temperature?"
      while continue == false do
         if response =~ /([0-9]+|zero|one|two|three|four|five|six|seven|eight|nine)/i
            response = $1
            # Standardize brightness numbers.
            response = response.sub("zero", "0")
            response = response.sub("one", "1")
            response = response.sub("two", "2")
            response = response.sub("three", "3")
            response = response.sub("four", "4")
            response = response.sub("five", "5")
            response = response.sub("six", "6")
            response = response.sub("seven", "7")
            response = response.sub("eight", "8")
            response = response.sub("nine", "9")
            if response.to_i < 50
               say "Actually, the thermostat can't go that low."
            elsif response.to_i > 99
               say "I thought you said you were hot! The thermostat can't even go above 99 degrees anyway."
            elsif response.to_i > theTemp
               say "I thought you said you were hot. Anyway..."
               
               increaseIndoorTemp(response.to_i)
               say "I've set the thermostat to bring the temperature to " + response + " degrees."
               continue = true
            else
               decreaseIndoorTemp(response.to_i)
               say "Okay. I've set the thermostat to drop the temperature to " + response + " degrees."
               continue = true
            end
         elsif response =~ /never|forget|stop|cancel|go away/i
            say "Okay. I won't change any thermostat settings."
            continue = true
         else
            say "Actually, I was hoping for a number."
         end
         if continue == false
            response = ask "What would be a comfortable temperature?"
         end
      end # While loop.
   end # End decrease indoor temperature.

   # CURRENT INDOOR TEMPERATURE
   listen_for /.*((indoor|inside|interior|hall).* temperature)|(temperature .*(indoor|inside|interior|hall).*)/i do
      
      currentIndoorTemp()
      
   end # End Current Indoor Temperature.
end

Again, the code, especially the custom action methods, are very specific to my Indigo database and devices, but I suppose it could be significantly modified for others to use. Thoughts?

Posted on
Mon Dec 17, 2012 1:47 pm
jottawa offline
Posts: 27
Joined: Aug 01, 2011

Re: SiriProxy plugin for Indigo

This is sooooo awesome, thanks so much for contributing this guys. I set it up on a Ubuntu VM for now but I'm thinking of trying it on the Mac instead.....still not sure which way to go.

Question for Matt. I had to disable authentication on the Indigo webserver to make the siriproky request able to talk to Indigo. I'm reluctant to leave my Indigo server password free. Is it possible to achieve this while leaving the authentication on?

Again, thanks so much to mreyn2005 & nsheldon

Posted on
Wed Dec 19, 2012 5:13 pm
mreyn2005 offline
User avatar
Posts: 161
Joined: Oct 06, 2006

Re: SiriProxy plugin for Indigo

Hey Jottawa,
Your observations are correct, I have my auth settings turned off on my Indigo Server when I develop my API's against it. It is because I never got auth working in my as3Indigo action script library (from my initial work on it circa 2008). I just released it on my github account a little while ago (it's posted on this forum), but I have not returned to fix the auth issue. So when I went to make this plugin, I was already running with the auth off.

I haven't gotten back to this plugin yet, but I do have plans to continue working on the feature in one form or another. I have a cleaned up version of this script, but it has a bug or two in it, and does not address the auth issue. I imagine it would be simple enough (with curl or with native ruby HTTP classes) to implement the Restful API BASIC auth method.

I am not sure when I will look at it, but I currently need to fix some stuff in the as3Indigo restful api, so maybe I will take up the auth flag while I'm in there and run it down for the flash implementation. At that point, I would know the format needed and it would be easy enough to get it into the ruby plugin for SiriProxy.

I'll keep you posted, no promises :-) Vacation for the next two weeks starts this weekend :-D

Happy Holidays!
Matt

Posted on
Fri Jan 18, 2013 8:18 pm
nexx offline
Posts: 56
Joined: Jun 27, 2010

Re: SiriProxy plugin for Indigo

Hi Guys, just bought a new iPhone which supports siri.

I'll start playing with it (through siri proxy) and indigo.. How's the progress?

Would you guys mind sharing your latest codes?

Posted on
Fri Jan 18, 2013 8:46 pm
jay (support) offline
Site Admin
User avatar
Posts: 18246
Joined: Mar 19, 2008
Location: Austin, Texas

Re: SiriProxy plugin for Indigo

You realize that effective use of this will require your Mac to be awake, right?

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Sat Jan 19, 2013 4:32 am
nexx offline
Posts: 56
Joined: Jun 27, 2010

Re: SiriProxy plugin for Indigo

jay (support) wrote:
You realize that effective use of this will require your Mac to be awake, right?



Yeah Jay, I already did change my scripts and went the awake route, I'll have to live with that as we talked on the other forum.

Instead of using sleepwatcher's sleep/wake hooks, I'm now using display_sleep/display_wakeup hooks to turn off/on some devices (like receiver, subwoofer, monitors, etc.). So far so good, everything is working with the new hooks.

Any new siri proxy/indigo code?

Does it work well? is it really useful?

Posted on
Sat Jan 19, 2013 12:46 pm
mreyn2005 offline
User avatar
Posts: 161
Joined: Oct 06, 2006

Re: SiriProxy plugin for Indigo

I had a very pleasant experience with it when I wrote my original script. I have fairly new hardware and that definitely can't hurt the overall experience as far as speed goes. I would make a request and with in ~2 seconds I would get my prompt for confirmation, upon confirming, another 2-3 seconds and a response from siri will be on screen and the command will have executed at the indigo server. With these response times, it definitely feels "usable" and "natural" (as natural as talking to Siri normally does).

I find even this one script more "useful" than most all the other UI work I've done or seen done elsewhere. I've said it before and I'll say it again: The holy grail of automation is NOT remote device access, but rather AI that does it for me when it is suppose to. By remote access I simply mean using your iPhone to turn on or off a light, to turn up or down the thermostat, etc. That is the current state of the art as far as interaction choices you have with your system. You can set up scripts that auto-foo stuff, that's cool (and necessary to go any further), but the lack of a solid chatbot interface certainly feels "missing" from my whiz bang system after using this script for a day. As a researcher striving for that holy grail, interacting with my system with Siri definitely feels more satisfying than using a GUI.

If this script were more robust and polished, I could see it easily replacing my "80% use case" for interacting with my home automation system, such that I would rarely use a computer GUI to interact with my system. I would rely on smart scripts, better hardware as it comes out (think Kinect for room occupancy detection), and the SiriProxy plugin to make manual corrections, telling my system what to do as needed in real time.

Due to the relative interest I've seen on this thread since I posted, I've decided to put this skunk-works project ahead in the ol' project hopper. That being said, I have some serious work setup for myself in that hopper already (and I have a day job). Thus, I have decided to hire an intern and have him work on this script. I will open-source the results of our collaboration either on this forum or on our company site, probably both. The project completion goal is for June, basically with in the next 6 months. I know this might be a long time for some folks, but beggars can't be choosers! ;-) It is my intern's last semester of his CS degree, so he will work on this project through the semester with my assistance.

There isn't that much to do, so maybe we'll get done sooner. Namely I can think of 3 things to make the script worthy of releasing it more formally:

1) Add Auth support
2) Use native HTTP Request code, don't use system calls to curl
3) Add the sprinkler control phrases Matt B requested, and work in any other useful control phrases that we can come up with.

I'll keep you posted on this thread.

Cheers!
Matt Reynolds
Mountain Labs, LLC

Posted on
Sat Jan 19, 2013 1:55 pm
jay (support) offline
Site Admin
User avatar
Posts: 18246
Joined: Mar 19, 2008
Location: Austin, Texas

Re: SiriProxy plugin for Indigo

Awesome - I haven't yet tried it but I may just have to when it's ready. What's the state of getting SiriProxy running? I looked at it quite a while back and it seemed pretty complicated. I'd love it if it were as easy as a double-click (or better yet an Indigo plugin).

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Sat Jan 19, 2013 2:19 pm
nexx offline
Posts: 56
Joined: Jun 27, 2010

Re: SiriProxy plugin for Indigo

mreyn2005 wrote:
I had a very pleasant experience with it when I wrote my original script. I have fairly new hardware and that definitely can't hurt the overall experience as far as speed goes. I would make a request and with in ~2 seconds I would get my prompt for confirmation, upon confirming, another 2-3 seconds and a response from siri will be on screen and the command will have executed at the indigo server. With these response times, it definitely feels "usable" and "natural" (as natural as talking to Siri normally does).
...
There isn't that much to do, so maybe we'll get done sooner. Namely I can think of 3 things to make the script worthy of releasing it more formally:

1) Add Auth support
2) Use native HTTP Request code, don't use system calls to curl
3) Add the sprinkler control phrases Matt B requested, and work in any other useful control phrases that we can come up with.

I'll keep you posted on this thread.

Cheers!
Matt Reynolds
Mountain Labs, LLC


Awesome news!

I agree with you about the state of art of automation. A chat box would be very nice and would bring the siri-feel to the proxied commands. About the "Auth Support" and Sprinkler control phrases I think it wouldn't add any value in my setup.. Thanks for such a good news!

It would be cool to see updates about the progress and know how we can contribute in any way. Unfortunately, I still didn't get my hands on the siri-capable device, so didn't setup your plugin to play with it..

I'm curious do you still use the siri proxy daily for automation purposes? Or just played with it some time and disabled the proxy?
Last edited by nexx on Sat Jan 19, 2013 7:47 pm, edited 1 time in total.

Posted on
Sat Jan 19, 2013 3:06 pm
mreyn2005 offline
User avatar
Posts: 161
Joined: Oct 06, 2006

Re: SiriProxy plugin for Indigo

Jay,
I found a descent blog post how-to here: http://www.funkyspacemonkey.com/install-siri-proxy.
If I recall correctly, I had to do step 15 before step 13, but otherwise ran into only minor issues. Took me less than an hour to get it up and running following that how-to.

Writing Ruby code as a n00b on the other hand... it took me much longer to write the script! And it's not even very well done as it stands...

In my opinion, for the relative cost to benefit of this plugin for SiriProxy, considering you are already a mac dependent platform, you guys should seriously consider making this a standard plugin for Indigo. The benefit, as I began to elaborate on in my previous post, is enormous for its power and elegant simplicity.

Yeah the cleaner the process the better. It would be great if an indigo plugin could install SiriProxy if it is not installed, then manage an instance of the daemon (start, stop, etc), and run the Violet plugin as the default plugin. I am sure all of this would be very possible to accomplish, just time and elbow grease!

Matt

Posted on
Sat Jan 19, 2013 3:55 pm
mreyn2005 offline
User avatar
Posts: 161
Joined: Oct 06, 2006

Re: SiriProxy plugin for Indigo

About the "Auth Support" and Sprinkler control phrases I think it wouldn't add any value in my setup..

Then the only thing you need is the cleaned up code for HTTP Requests, which isn't really a need so much as an optimization - probably not important for you either.
So you're all set then! I definitely want to support Auth, it is important. Using the native HTTP Requests should make it easier to add a BASIC header for authentication.

It would be cool to see updates about the progress and know how we can contribute in any way.

It would be nice to get feedback from people when the time is right. Or, you know, if you happen to be a Ruby developer and actually know what you're doing (cause I don't), that'd be a big help too!

Unfortunately, I still didn't get my hands on the siri-capable device, so didn't setup your plugin to play with it..

With out appropriate hardware, it will be hard to contribute indeed! ;-)

I'm curious do you still use the siri proxy daily for automation purposes? Or just played with it some time and disabled the proxy?

I am currently out of town on contract, so my apartment here does not have any hardware in it. I just use the Indigo software running on my laptop locally to simulate my real home environment. At home, I have a dedicated mac mini running indigo, with plenty of hardware to go with. Mostly, my goal while being away has been to improve my personal project's status to beta 1.0. This is what else occupies the majority of my hopper at this time. In the past 6 months I have made descent progress towards a 1.0 beta. With another 6 I think I can get it there. That being said, Violet meshes well with the overall goal of this personal project. Indeed, as I alluded to previously, I am seeing the light and realizing that certain GUI's in this personal project may not get much play if I have Violet as an interface option. That is one of the main reasons I have moved up the priority of Violet in the queue and am putting dedicated resources towards it.

Cheers!
Matt

Who is online

Users browsing this forum: No registered users and 3 guests