Good progress is being made... items 2 and 3 in the list above are working. I am just trying to figure out the auth and it's giving me some issues. Stay tuned!
Matt
mreyn2005 wrote:Good progress is being made... items 2 and 3 in the list above are working. I am just trying to figure out the auth and it's giving me some issues. Stay tuned!
Matt
http_request.add_field 'Authorization', auth
# -*- encoding: utf-8 -*-
$:.push File.expand_path("../lib", __FILE__)
Gem::Specification.new do |s|
s.name = "siriproxy-violet"
s.version = "1.0"
s.authors = ["Matthew Reynolds, Mountain Labs"]
s.email = ["matt@mtnlabs.com"]
s.homepage = "mtnlabs.com"
s.summary = %q{SiriProxy plugin for controlling the Indigo Automation Server}
s.description = %q{This plugin can control your Indigo system through voice control phrases issued to SiriProxy}
s.rubyforge_project = ""
s.files = `git ls-files 2> /dev/null`.split("\n")
s.test_files = `git ls-files -- {test,spec,features}/* 2> /dev/null`.split("\n")
s.executables = `git ls-files -- bin/* 2> /dev/null`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]
# specify any dependencies here; for example:
s.add_runtime_dependency "addressable"
s.add_runtime_dependency "net-http-digest_auth"
end
########################################################################################################################
# Mountain Labs, LLC
# mtnlabs.com
# Author: Matthew Reynolds
# matt at mtnlabs dot com
# Indigo Violet SiriProxy plugin 1.0 beta for controlling your Indigo Home Automation server
########################################################################################################################
require 'cora'
require 'siri_objects'
require 'pp'
require 'addressable/uri'
require 'net/http'
require 'net/http/digest_auth'
class SiriProxy::Plugin::Violet < SiriProxy::Plugin
## Pick something that is:
#1) the most common spelling of an actual name or word
#2) a name or word that is easy to recognize and has a sharp pronunciation (violet does not work well)
BOT_NAME = 'Indigo Violet'
# Indigo Host (address and port of your Indigo Restful Interface)
INDIGO_WEB_SERVER = 'http://127.0.0.1:8176'
# Indigo Digest Auth properties
INDIGO_REALM = 'Indigo Control Server'
INDIGO_USER = 'indigo'
INDIGO_PASSWORD = 'indigo'
INDIGO_SPRINKLER_DEVICE = 'irrmasterpro'
INDIGO_THERMOSTAT_DEVICE = 'thermostat'
def initialize(config)
#process custom configuration options here!
end
#############################################################
# Methods
#############################################################
# Create a device uri string
# Return the device uri string
def get_device_url(device_name)
device_url = '/devices/'+device_name
device_url = Addressable::URI.parse(device_url).normalize.to_str
device_url
end
# Create an instance of URI
# Return the URI instance
def get_device_uri(device_name)
device_url = INDIGO_WEB_SERVER + get_device_url(device_name)
device_uri = Addressable::URI.parse(device_url)
device_uri.user = INDIGO_USER
device_uri.password = INDIGO_PASSWORD
device_uri
end
# Create an action group uri string
# Return the action group uri string
def get_action_group_url(action_group_name)
action_group_url = '/actions/'+action_group_name+'?_method=execute'
action_group_url = Addressable::URI.parse(action_group_url).normalize.to_str
action_group_url
end
# Create an instance of URI
# Return the URI instance
def get_action_group_uri(action_group_name)
action_group_url = INDIGO_WEB_SERVER + get_action_group_url(action_group_name)
action_group_uri = Addressable::URI.parse(action_group_url)
action_group_uri.user = INDIGO_USER
action_group_uri.password = INDIGO_PASSWORD
action_group_uri
end
# Create and send an authenticated HTTP Get call
# Return the HTTP Response
def do_authenticated_get(indigo_uri)
http = Net::HTTP.new indigo_uri.host, indigo_uri.port
#http.set_debug_output $stderr
# Make initial request that should produce a 401 Unauthorized
# The 401 Unauthorized response contains important auth information,
# bits of which are used later to create an authorized session
http_request = Net::HTTP::Get.new indigo_uri.request_uri
http_response = http.request http_request
# Support for Digest Authentication
digest_auth = Net::HTTP::DigestAuth.new
# Clean the quotes from the algorithm=\"MD5\" in the original http_response's www-authenticate string
# The spec does not include quotes in this flag, having them can cause issues
# http://stackoverflow.com/questions/10770478/unknown-algorithm-md5-using-net-http-digest-auth
www_auth_response = http_response['www-authenticate']
www_auth_response["algorithm=\"MD5\""] = "algorithm=MD5"
# Construct the appropriate Authentication header string
auth = digest_auth.auth_header indigo_uri, www_auth_response, 'GET'
# Create the new EXECUTE request with the proper Authorization header
http_request = Net::HTTP::Get.new indigo_uri.request_uri
http_request.add_field 'Authorization', auth
# Make the second (authenticated) call, return the http response
http.request http_request
end
# Create and send an authenticated HTTP PUT call
# Return the HTTP Response
def do_authenticated_put(indigo_uri, body)
http = Net::HTTP.new indigo_uri.host, indigo_uri.port
#http.set_debug_output $stderr
# Make initial request that should produce a 401 Unauthorized
# The 401 Unauthorized response contains important auth information,
# bits of which are used later to create an authorized session
http_request = Net::HTTP::Get.new indigo_uri.request_uri
http_response = http.request http_request
# Support for Digest Authentication
digest_auth = Net::HTTP::DigestAuth.new
# Clean the quotes from the algorithm=\"MD5\" in the original http_response's www-authenticate string
# The spec does not include quotes in this flag, having them can cause issues
# http://stackoverflow.com/questions/10770478/unknown-algorithm-md5-using-net-http-digest-auth
www_auth_response = http_response['www-authenticate']
www_auth_response["algorithm=\"MD5\""] = "algorithm=MD5"
# Construct the appropriate Authentication header string
auth = digest_auth.auth_header indigo_uri, www_auth_response, 'PUT'
# Create the new PUT request with the proper Authorization header
http_request = Net::HTTP::Put.new indigo_uri.request_uri
http_request.content_type = 'multipart/form-data'
http_request.set_form_data(body)
http_request.add_field 'Authorization', auth
# Make the second (authenticated) call, return the http response
http.request http_request
end
# Create a string response to send back to the user, given the HTTP response from the indigo web server
# Return the string
def get_bot_response(http_response)
case http_response.code
when "303"
"Ok"
when "401"
"I am unauthorized"
when "200"
"I am unable to find that device"
when "404"
"I am unable to find the automation system"
else
"I received an unknown response from the automation system"
end
end
#############################################################
# Filters
#############################################################
#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
## 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}"
#############################################################
# Listen Control Phrases
#############################################################
listen_for /test siri proxy/i do
# standard test
say "You may call me "+BOT_NAME
request_completed
end
listen_for /test #{BOT_NAME}/i do
# custom test
begin
devices_url = INDIGO_WEB_SERVER + '/devices.xml'
devices_uri = Addressable::URI.parse(devices_url)
devices_uri.user = INDIGO_USER
devices_uri.password = INDIGO_PASSWORD
http_response = do_authenticated_get(devices_uri)
case http_response.code
when "401"
status = "Unauthorized"
when "200"
status = "Ready"
when "404"
status = "Unable to find the automation system"
else
status = "Unable to negotiate with the automation system"
end
say 'Indigo Violet plugin 1.0 beta for SiriProxy. System Status: '+status
rescue Exception=>e
puts e.exception
ensure
request_completed
end
end
listen_for /hello #{BOT_NAME}/i do
# standard greeting
say 'Hello.'
request_completed
end
#Turn On
listen_for /turn on(?: the)? ([a-z 1-9]*)/i do |device_name|
device_name.strip!
begin
device_uri = get_device_uri(device_name)
http_response = do_authenticated_put(device_uri, {"isOn" => "True"})
say get_bot_response(http_response)
rescue Exception=>e
puts e.exception
ensure
request_completed
end
end
#Turn Off
listen_for /turn off(?: the)? ([a-z 0-9]*)/i do |device_name|
device_name.strip!
begin
device_uri = get_device_uri(device_name)
http_response = do_authenticated_put(device_uri, {"isOn" => "False"})
say get_bot_response(http_response)
rescue Exception=>e
puts e.exception
ensure
request_completed
end
end
#Execute
listen_for /execute ([a-z 0-9]*)/i do |action_group_name|
action_group_name.strip!
begin
action_group_uri = get_action_group_uri(action_group_name)
http_response = do_authenticated_get(action_group_uri)
say get_bot_response(http_response)
rescue Exception=>e
puts e.exception
ensure
request_completed
end
end
#Thermostat
listen_for /set(?: the)? thermostat to ([0-9]*[0-9])/i do |degrees|
degrees.strip!
begin
device_uri = get_device_uri(INDIGO_THERMOSTAT_DEVICE)
http_response = do_authenticated_put(device_uri, {"setpointHeat" => degrees})
say get_bot_response(http_response)
rescue Exception=>e
puts e.exception
ensure
request_completed
end
end
#Sprinklers
listen_for /([a-z]*) sprinklers/i do |action|
action.strip!
begin
if (action =~ /pause/i or action =~ /resume/i)
device_uri = get_device_uri(INDIGO_SPRINKLER_DEVICE)
http_response = do_authenticated_put(device_uri, {"activeZone" => action})
say get_bot_response(http_response)
else
say "I don't understand that sprinkler action request"
end
rescue Exception=>e
puts e.exception
ensure
request_completed
end
end
#Brightness
listen_for /set(?: the)? ([a-z 0-9]*) brightness at ([0-9]*[0-9])(?: percent)?/i do |device_name, dim_value|
device_name.strip!
dim_value.strip!
begin
device_uri = get_device_uri(device_name)
http_response = do_authenticated_put(device_uri, {"brightness" => dim_value})
say get_bot_response(http_response)
rescue Exception=>e
puts e.exception
ensure
request_completed
end
end
#Toggle
listen_for /toggle(?: the)? ([a-z 1-9]*)/i do |device_name|
device_name.strip!
begin
device_uri = get_device_uri(device_name)
http_response = do_authenticated_put(device_uri, {"toggle" => "1"})
say get_bot_response(http_response)
rescue Exception=>e
puts e.exception
ensure
request_completed
end
end
end
Using Siri causes a whole bunch of the following messages, followed by SiriProxy crashing!
Create server for iPhone connection
start conn #<SiriProxy::Connection::Iphone:0x966a400 @signature=880, @processed_headers=false, @output_buffer="", @input_buffer="", @unzipped_input="", @unzipped_output="", @unzip_stream=#<Zlib::Inflate:0x9669640>, @zip_stream=#<Zlib::Deflate:0x96695dc>, @consumed_ace=false, @name="iPhone", @ssled=false>
[Info - Plugin Manager] Plugins laoded: [#<SiriProxy::Plugin::Example:0x968a818 @manager=#<SiriProxy::PluginManager:0x9685750 @plugins=[...]>>]
This is actually really common (but can be tricky to fix). The problem is that your SiriProxy server is using your tainted DNS server. So what happens is this:
Your iPhone connects to your server, thinking it's guzzoni.apple.com
Your server connects to itself, thinking that it's guzzoni.apple.com
Your server thinks another iPhone has connected, and repeats step 2.
This goes on forever, or at least a second or two before the server up and dies. The trick is that you need to make sure your server isn't connecting to itself when it requests a connection to guzzoni.apple.com. This is actually the default behavior, but many people accidentally mess things up by either (1) setting up their server to use itself as a DNS server (while using dnsmasq to taint the entry for guzzoni.apple.com), or (2) putting their server on a network where the DNS server issued by DHCP is tainted to point to the wrong guzzoni.apple.com.
So the fix for this varies based on your setup, but one possible fix for scenario 1 (above) on many *NIX machines is to edit /etc/resolve.conf and change the nameserver entry to 8.8.8.8 (one of Google's public DNS servers). Do this and then restart networking (or just restart the computer) and things should start working.
Your network setup may be different. This is THE most complex part of setting up SiriProxy (getting DNS set up correctly). So once you have this working, you are probably home free. Keep with it, good luck, and have fun!
[Info - Guzzoni] Received Object: SpeechRecognized
[Info - Plugin Manager] Processing 'Toggle kitchen light'
[Info - Plugin Manager] Processing plugin Example
[Info - Plugin Manager] Matches (?i-mx:toggle(?: the)? ([a-z 1-9]*))
[Info - Plugin Manager] Applicable states:
[Info - Plugin Manager] Current state:
[Info - Plugin Manager] Matches, executing block
undefined method `[]=' for nil:NilClass
[Info - Plugin Manager] Sending Request Completed
[Info - iPhone] Received Object: SpeechPacket
[Info - iPhone] Received Object: FinishSpeech
[Info - Guzzoni] Received Object: SpeechRecognized
[Info - Plugin Manager] Processing 'Toggle the kitchen lights'
[Info - Plugin Manager] Processing plugin Example
[Info - Plugin Manager] Matches (?i-mx:toggle(?: the)? ([a-z 1-9]*))
[Info - Plugin Manager] Applicable states:
[Info - Plugin Manager] Current state:
[Info - Plugin Manager] Matches, executing block
opening connection to 192.168.1.30:8176...
opened
<- "GET /devices/kitchen%20lights HTTP/1.1\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: 192.168.1.30:8176\r\n\r\n"
-> "HTTP/1.1 200 OK\r\n"
-> "Content-Length: 655\r\n"
-> "Content-Encoding: gzip\r\n"
-> "Vary: Accept-Encoding\r\n"
-> "Server: IndigoWebServer/5.0\r\n"
-> "Allow: GET, HEAD, POST, PUT\r\n"
-> "Date: Wed, 22 May 2013 03:45:41 GMT\r\n"
-> "Content-Type: text/html;charset=utf-8\r\n"
-> "Connection: close\r\n"
-> "\r\n"
reading 655 bytes...
-> "\x1F\x8B\b\x00e?\x9CQ\x02\xFF\x95\x95_s\x9A@\x14\xC5\x9F\x93O\xB1e&o\x85\xE5\x8F\x18\xB1\xE8L\x82\xDA8\xA3\xD5\t\xA4i\xFB\xB6\xC2Uv\x82\x8B\xD9]c\xFC\xF6]\xD0\xB4\x1DP\xAB/\nx~\xE7pw\xBDw\xFDO\xBDI\x10\xFD\x9C\xF6Q*\x97\x19\x9A>\xDD\x8F\x86\x01\xD2t\x8C\x9F\x9D\x00\xE3^\xD4C?\x1E\xA2\xF1\bY\x86\x89B\xC9i,1\xEE\x7F\xD3\x90\x96J\xB9jc\xBC\xD9l\x8C\x8Dc\xE4|\x81\xA3G\xFC^\xB8X\x05\xB6\xBF\xD4E\xC9\x18\x89L\xB4\xEE\xB5_\x86\xBC/3&:\a\f,\xCF\xF3v\x9CV\x88\xDA\x19a\x8B\x8E\x06L\x91W~\n$Q\xDFW\xBE\xA42\x83\xEE\x90%t\x91\xA3\x87|\t(\xC8\x99\xE4y\x86B\xE0o\xC0}\xBCS\x14\xDA%H\x82\x8A \x1D^\xD7\xF4\xAD\xA3\x15R`R\x8F\xB6+\xD0P\xBC\xBB\xEBh\x12\xDE%.\x82\xBF\xA08%\\\x80\xEC\xAC\xE5\\oi\b\x17\xD9x\x1F\xEE\xCF\xF2d[\x1A\xAB\xB2r\xB6\xE82\xA2\xE2\xDB\xC8\xC7\xFB{\xF5\xD3\v\x95q\n\fet\x91J\xE1\xCFxi\xF1\x87 I\xC2A\x88\nd5\xCCF\xEB\xD6:\xA6V\v_\x05\\\xA390\x82\xDB\x1A1\xE3E.;\x10a\x9A5q\x9C\x11!\x86\xBD\x8A\xD2\xAE\xE9\x12\x10qE4\xDB\xAA\xD5\x9B\xCF\x01\x0E\x88\xDF\xA6<\x97y\xACv\xA4\xF2\nu-\x15\xAB\x8Cl\x87\xECiX\xD1F|\r\xC7\xE4#u\x1DJ\"\xA1^\xE2\xCD1\xE6\x91l\x8E 5\"%\xA2\x94Fyo\xC7\x9E\xF3j4\xA9\xA8\\\xBBe5]\xDB\xA9\xFBS1a\xE7X\xAA\xCD\x91A\xAAz\x00\xAA\xDE\r\xDBn\xDCZM\xCF;\xC5\xF4T\t\xF5?\x8EmZ\x8En\xBA\xBAm\x9Db\x1F\a\x81\xE38\xDE1\xD6\x8EL\xB3m5\xDA\xAE\xF7\xEB?.-\xDB\xAE\x98<C\xF2\x19\xA9\xA7c\xB5\xAE\x85!\xFA\xB0B_\xC7\xD1)\xB7\x88.\x0F\xD4c\xBA{z:\xAE\xC1R5yE\x1En\x8A\xF6\x1CQ\x16\xA3\x1E].\xD5\xB48\x04\r2\xB2\xA8\xF5\x8FcY\a\xC5\xE1z\xB5\xCA\xB9\x14\xCA\xEF\x9C]\xFD\x17\xE93\xE0\x8B\xED\x18$T\xCB\x1A\x90L\x9Cf\x1F\xBE\xDF\x05\x17C\xC3\xC9\xC5\xC8\x84M\xE6\xF3K\v\vW\x00\xC9\xC7X\xBE41\\q\xCA^\xB23\xD7DM|q\xBF\xAD5\xB6\xDB\xFC\xABTG\x00\x87yG\xC3j4\xD1\x18\x04\xDE\xCF\xE8\e\xDB\xDCMi\xA3\x18\xFD\x18\x12*\xB5n_}\xA2;\xA9\x8E\xAD\xD9Z\x82\xF01)O\x80\xDD\xE4\xF7\xCBC\xA2{\xFD\e\x02\xEFn|7\a\x00\x00"
read 655 bytes
Conn close
undefined method `[]=' for nil:NilClass
[Info - Plugin Manager] Sending Request Completed
# -*- encoding: utf-8 -*-
$:.push File.expand_path("../lib", __FILE__)
Gem::Specification.new do |s|
s.name = "siriproxy-example"
s.version = "0.0.1"
s.authors = ["plamoni"]
s.email = [""]
s.homepage = ""
s.summary = %q{An Example Siri Proxy Plugin}
s.description = %q{This is a "hello world" style plugin. It simply intercepts the phrase "text siri proxy" and responds with a message about the proxy being up and running. This is good base code for other plugins. }
s.rubyforge_project = "siriproxy-example"
s.files = `git ls-files 2> /dev/null`.split("\n")
s.test_files = `git ls-files -- {test,spec,features}/* 2> /dev/null`.split("\n")
s.executables = `git ls-files -- bin/* 2> /dev/null`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]
# specify any dependencies here; for example:
# s.add_development_dependency "rspec"
# s.add_runtime_dependency "rest-client"
s.add_runtime_dependency "addressable"
s.add_runtime_dependency "net-http-digest_auth"
end
require 'cora'
require 'siri_objects'
require 'pp'
require 'addressable/uri'
require 'net/http'
require 'net/http/digest_auth'
#######
# This is a "hello world" style plugin. It simply intercepts the phrase "test siri proxy" and responds
# with a message about the proxy being up and running (along with a couple other core features). This
# is good base code for other plugins.
#
# Remember to add other plugins to the "config.yml" file if you create them!
######
class SiriProxy::Plugin::Example < SiriProxy::Plugin
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 /where am i/i do
say "Your location is: #{location.address}"
end
#listen_for /test siri proxy/i do
# say "Siri Proxy is up and running Brian!" #say something to the user!
# request_completed #always complete your request! Otherwise the phone will "spin" at the user!
#end
#Demonstrate that you can have Siri say one thing and write another"!
listen_for /you don't say/i do
say "Sometimes I don't write what I say", spoken: "Sometimes I don't say what I write"
end
#demonstrate state change
listen_for /siri proxy test state/i do
set_state :some_state #set a state... this is useful when you want to change how you respond after certain conditions are met!
say "I set the state, try saying 'confirm state change'"
request_completed #always complete your request! Otherwise the phone will "spin" at the user!
end
listen_for /confirm state change/i, within_state: :some_state do #this only gets processed if you're within the :some_state state!
say "State change works fine!"
set_state nil #clear out the state!
request_completed #always complete your request! Otherwise the phone will "spin" at the user!
end
#demonstrate asking a question
listen_for /siri proxy test question/i do
response = ask "Is this thing working?" #ask the user for something
if(response =~ /yes/i) #process their response
say "Great!"
else
say "You could have just said 'yes'!"
end
request_completed #always complete your request! Otherwise the phone will "spin" at the user!
end
#demonstrate capturing data from the user (e.x. "Siri proxy number 15")
listen_for /siri proxy number ([0-9,]*[0-9])/i do |number|
say "Detected number: #{number}"
request_completed #always complete your request! Otherwise the phone will "spin" at the user!
end
#demonstrate injection of more complex objects without shortcut methods.
listen_for /test map/i do
add_views = SiriAddViews.new
add_views.make_root(last_ref_id)
map_snippet = SiriMapItemSnippet.new
map_snippet.items << SiriMapItem.new
utterance = SiriAssistantUtteranceView.new("Testing map injection!")
add_views.views << utterance
add_views.views << map_snippet
#you can also do "send_object object, target: :guzzoni" in order to send an object to guzzoni
send_object add_views #send_object takes a hash or a SiriObject object
request_completed #always complete your request! Otherwise the phone will "spin" at the user!
end
## Pick something that is:
#1) the most common spelling of an actual name or word
#2) a name or word that is easy to recognize and has a sharp pronunciation (violet does not work well)
BOT_NAME = 'Indigo'
# Indigo Host (address and port of your Indigo Restful Interface)
INDIGO_WEB_SERVER = 'http://192.168.1.30:8176'
# Indigo Digest Auth properties
INDIGO_REALM = 'Indigo Control Server'
INDIGO_USER = 'indigo'
INDIGO_PASSWORD = 'indigo'
INDIGO_SPRINKLER_DEVICE = 'Sprinklers'
INDIGO_THERMOSTAT_DEVICE = 'Thermostat Downstairs'
def initialize(config)
#process custom configuration options here!
end
#############################################################
# Methods
#############################################################
# Create a device uri string
# Return the device uri string
def get_device_url(device_name)
device_url = '/devices/'+device_name
device_url = Addressable::URI.parse(device_url).normalize.to_str
device_url
end
# Create an instance of URI
# Return the URI instance
def get_device_uri(device_name)
device_url = INDIGO_WEB_SERVER + get_device_url(device_name)
device_uri = Addressable::URI.parse(device_url)
device_uri.user = INDIGO_USER
device_uri.password = INDIGO_PASSWORD
device_uri
end
# Create an action group uri string
# Return the action group uri string
def get_action_group_url(action_group_name)
action_group_url = '/actions/'+action_group_name+'?_method=execute'
action_group_url = Addressable::URI.parse(action_group_url).normalize.to_str
action_group_url
end
# Create an instance of URI
# Return the URI instance
def get_action_group_uri(action_group_name)
action_group_url = INDIGO_WEB_SERVER + get_action_group_url(action_group_name)
action_group_uri = Addressable::URI.parse(action_group_url)
action_group_uri.user = INDIGO_USER
action_group_uri.password = INDIGO_PASSWORD
action_group_uri
end
# Create and send an authenticated HTTP Get call
# Return the HTTP Response
def do_authenticated_get(indigo_uri)
http = Net::HTTP.new indigo_uri.host, indigo_uri.port
http.set_debug_output $stderr
# Make initial request that should produce a 401 Unauthorized
# The 401 Unauthorized response contains important auth information,
# bits of which are used later to create an authorized session
http_request = Net::HTTP::Get.new indigo_uri.request_uri
http_response = http.request http_request
# Support for Digest Authentication
digest_auth = Net::HTTP::DigestAuth.new
# Clean the quotes from the algorithm=\"MD5\" in the original http_response's www-authenticate string
# The spec does not include quotes in this flag, having them can cause issues
# http://stackoverflow.com/questions/10770478/unknown-algorithm-md5-using-net-http-digest-auth
www_auth_response = http_response['www-authenticate']
www_auth_response["algorithm=\"MD5\""] = "algorithm=MD5"
# Construct the appropriate Authentication header string
auth = digest_auth.auth_header indigo_uri, www_auth_response, 'GET'
# Create the new EXECUTE request with the proper Authorization header
http_request = Net::HTTP::Get.new indigo_uri.request_uri
#http_request.add_field 'Authorization', auth
# Make the second (authenticated) call, return the http response
http.request http_request
end
# Create and send an authenticated HTTP PUT call
# Return the HTTP Response
def do_authenticated_put(indigo_uri, body)
http = Net::HTTP.new indigo_uri.host, indigo_uri.port
http.set_debug_output $stderr
# Make initial request that should produce a 401 Unauthorized
# The 401 Unauthorized response contains important auth information,
# bits of which are used later to create an authorized session
http_request = Net::HTTP::Get.new indigo_uri.request_uri
http_response = http.request http_request
# Support for Digest Authentication
digest_auth = Net::HTTP::DigestAuth.new
# Clean the quotes from the algorithm=\"MD5\" in the original http_response's www-authenticate string
# The spec does not include quotes in this flag, having them can cause issues
# http://stackoverflow.com/questions/10770478/unknown-algorithm-md5-using-net-http-digest-auth
www_auth_response = http_response['www-authenticate']
www_auth_response["algorithm=\"MD5\""] = "algorithm=MD5"
# Construct the appropriate Authentication header string
auth = digest_auth.auth_header indigo_uri, www_auth_response, 'PUT'
# Create the new PUT request with the proper Authorization header
http_request = Net::HTTP::Put.new indigo_uri.request_uri
http_request.content_type = 'multipart/form-data'
http_request.set_form_data(body)
#http_request.add_field 'Authorization', auth
# Make the second (authenticated) call, return the http response
http.request http_request
end
# Create a string response to send back to the user, given the HTTP response from the indigo web server
# Return the string
def get_bot_response(http_response)
case http_response.code
when "303"
"Ok"
when "401"
"I am unauthorized"
when "200"
"I am unable to find that device"
when "404"
"I am unable to find the automation system"
else
"I received an unknown response from the automation system"
end
end
#############################################################
# Filters
#############################################################
#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
## 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}"
#############################################################
# Listen Control Phrases
#############################################################
listen_for /test siri proxy/i do
# standard test
say "You may call me "+BOT_NAME
request_completed
end
listen_for /test #{BOT_NAME}/i do
# custom test
begin
devices_url = INDIGO_WEB_SERVER + '/devices.xml'
devices_uri = Addressable::URI.parse(devices_url)
devices_uri.user = INDIGO_USER
devices_uri.password = INDIGO_PASSWORD
http_response = do_authenticated_get(devices_uri)
case http_response.code
when "401"
status = "Unauthorized"
when "200"
status = "Ready"
when "404"
status = "Unable to find the automation system"
else
status = "Unable to negotiate with the automation system"
end
say 'Indigo Violet plugin 1.0 beta for SiriProxy. System Status: '+status
rescue Exception=>e
puts e.exception
ensure
request_completed
end
end
listen_for /hello #{BOT_NAME}/i do
# standard greeting
say 'Hello.'
request_completed
end
#Turn On
listen_for /turn on(?: the)? ([a-z 1-9]*)/i do |device_name|
device_name.strip!
begin
device_uri = get_device_uri(device_name)
http_response = do_authenticated_put(device_uri, {"isOn" => "True"})
say get_bot_response(http_response)
rescue Exception=>e
puts e.exception
ensure
request_completed
end
end
#Turn Off
listen_for /turn off(?: the)? ([a-z 0-9]*)/i do |device_name|
device_name.strip!
begin
device_uri = get_device_uri(device_name)
http_response = do_authenticated_put(device_uri, {"isOn" => "False"})
say get_bot_response(http_response)
rescue Exception=>e
puts e.exception
ensure
request_completed
end
end
#Execute
listen_for /execute ([a-z 0-9]*)/i do |action_group_name|
action_group_name.strip!
begin
action_group_uri = get_action_group_uri(action_group_name)
http_response = do_authenticated_get(action_group_uri)
say get_bot_response(http_response)
rescue Exception=>e
puts e.exception
ensure
request_completed
end
end
#Thermostat
listen_for /set(?: the)? thermostat to ([0-9]*[0-9])/i do |degrees|
degrees.strip!
begin
device_uri = get_device_uri(INDIGO_THERMOSTAT_DEVICE)
http_response = do_authenticated_put(device_uri, {"setpointHeat" => degrees})
say get_bot_response(http_response)
rescue Exception=>e
puts e.exception
ensure
request_completed
end
end
#Sprinklers
listen_for /([a-z]*) sprinklers/i do |action|
action.strip!
begin
if (action =~ /pause/i or action =~ /resume/i)
device_uri = get_device_uri(INDIGO_SPRINKLER_DEVICE)
http_response = do_authenticated_put(device_uri, {"activeZone" => action})
say get_bot_response(http_response)
else
say "I don't understand that sprinkler action request"
end
rescue Exception=>e
puts e.exception
ensure
request_completed
end
end
#Brightness
listen_for /set(?: the)? ([a-z 0-9]*) brightness at ([0-9]*[0-9])(?: percent)?/i do |device_name, dim_value|
device_name.strip!
dim_value.strip!
begin
device_uri = get_device_uri(device_name)
http_response = do_authenticated_put(device_uri, {"brightness" => dim_value})
say get_bot_response(http_response)
rescue Exception=>e
puts e.exception
ensure
request_completed
end
end
#Toggle
listen_for /toggle(?: the)? ([a-z 1-9]*)/i do |device_name|
device_name.strip!
begin
device_uri = get_device_uri(device_name)
http_response = do_authenticated_put(device_uri, {"toggle" => "1"})
say get_bot_response(http_response)
rescue Exception=>e
puts e.exception
ensure
request_completed
end
end
end
jay (support) wrote: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).
mreyn2005 wrote:It's that easy! Clear as mud?
Matt
Users browsing this forum: No registered users and 9 guests