Posted to tcl by Napier at Thu Jun 26 11:20:27 GMT 2014view raw
- # --------------------------------------------
- # Nest & Smoke+CO TCL Integration Library
- # Connects to the Nest API for control &
- # monitoring of NEST Products.
- #
- # Author: Automation Apps
- # --------------------------------------------
- # Main Command Syntax:
- # -fanTimer <start,stop> -mode <off, heat, cool, heat-cool> -
- #
-
- enableLOG 0
- set varIfMRXMAC "000"
-
- # Include Packages in the includePackages List
- set includePackages "http tls json json::write tdom uri"
- foreach {pkg} $includePackages {package require $pkg}
-
- tls::init -tls1 true
- http::register https 443 tls::socket
-
- namespace eval API {
- # Be Sure to Set your Client Secret & Client ID
- variable FB_Auth "auth=c.DWjy5mnOvgHcJ1NdKisooOZEIyO5dpVacU8tC5B9Me6QtkQ7vtHOqwYMdneHz8pnNf3raKOQlVjaIVmyQCZFnAxC5H7vKGP5WK3IKTTX3l8Gd0pBB0B1th2Q5cOWZFNWOJiq8IPHZPv6U5HB"
- variable authState $::varIfMRXMAC
- variable baseURL "https://developer-api.nest.com"
- variable basePort 443
- variable client_secret "wCJzrq2VuyWcH70DjENWpjupi"
- variable client_id "7c3fe371-5740-48ac-a1b2-74df90145813"
- variable authRequestURL "https://home.nest.com/login/oauth2?client_id=${client_id}&state=${authState}"
- variable authorizeURL "https://api.home.nest.com/oauth2/access_token?code=|AUTHPIN|&client_id=${client_id}&client_secret=${client_secret}&grant_type=authorization_code"
- variable forecast "https://home.nest.com/api/0.1/weather/forecast/${zip},${country}"
- }
-
- namespace eval Web {
-
- variable RequestData [dict create]
-
- proc Send {args} {
- # Syntax:
- # -method $METHOD -url $URL -params $PARAMS -suffix $URL-Suffix -port $port -body $body -request $request
- # -request feeds the RequestData Variable to Identify to the Callback
- # Web::Send -method PATCH -url https://www.url.com -params "Hi Hello" -suffix "devices/hello" -body $json -port 9553 would PATCH:
- # https://www.url.com:9553/devices/hello?auth={AUTH CODE}&hi=hello with the value of $json as the body
- LOG "Beginning Web::Send $args"
- set tempDict $args
- if {$::API::FB_Auth == 0} {LOG "OAuth Required Before Continuing"; #TODO: Build Re-Auth Service}
- if {![dict exists $tempDict -suffix]} {LOG "No Suffix Detected"; dict set tempDict -suffix ""}
- if {![dict exists $tempDict -url]} {LOG "No URL using Default"; dict set tempDict -url $::API::baseURL}
- if {![dict exists $tempDict -method]} {LOG "No Method Defined, Using GET"; dict set tempDict -method "GET"}
- if {[dict exists $tempDict -params]} {LOG "Formatting Query"; dict set tempDict -params "&[::http::formatQuery {*}[dict get $tempDict -params]]"
- } else {LOG "No Params Detected"; dict set tempDict -params ""}
- if {[dict exists $tempDict -port]} {LOG "Custom Port Detected: [dict get $tempDict -port]"; http::register https [dict get $tempDict -port] tls::socket
- } else {http::register https $::API::basePort tls::socket}
- if {![dict exists $tempDict -body]} {LOG "No Body Detected"; dict set tempDict -body ""}
- if {![dict exists $tempDict -request]} {dict set tempDict -request "None"}
- set ::Web::RequestData $tempDict
- if {[dict get $tempDict -method] == "GET"} {set token [http::geturl [dict get $tempDict -url]/[dict get $tempDict -suffix]?${::API::FB_Auth}[dict get $tempDict -params] -command ::Web::httpCallback]
- } else {set token [http::geturl [dict get $tempDict -url]/[dict get $tempDict -suffix]?${::API::FB_Auth}[dict get $tempDict -params] \
- -method [dict get $tempDict -method] -query [dict get $tempDict -body] -command ::Web::httpCallback]
- }
- }
-
- proc httpCallback {token} {
- set data $::Web::RequestData
- set response [dict create data [http::data $token] code [http::code $token]]
- LOG "----- RECEIVING DATA -----"
- LOG "$response"
- LOG "--------------------------"
- puts $token
- if {[string match {30[1237]} [::http::ncode $token]]} {LOG "Redirect Required!"; ::Web::startRedirect $token
- } elseif {[::http::ncode $token] == 200} {LOG "Successfull Call Detected"
- switch -- [dict get $data -request] {
- Query {::Nest::SetStatus [::json::json2dict [dict get $response data]]}
- default {LOG "Unknown or Undefined Request, Saving Value to ::Nest::StatusUndefined"; set ::Nest::StatusUndefined $data}
- }
- } elseif {[::http::ncode $token] == 429} {LOG "Too Many Requests Error Received"}
- }
-
- proc startRedirect {token} {
- set data $::Web::RequestData
- array set meta [set ${token}(meta)]
- LOG "[parray {*}$token]"
- http::cleanup $token
- if {![info exist meta(Location)]} {LOG "No Redirection Indicator?"; return}
- array set uri [::uri::split $meta(Location)]
- LOG "URI:"; LOG "[parray uri]"
- unset meta
- if {$uri(host) == ""} {set uri(host) $uri(host)}
- set url [eval ::uri::join [array get uri]]
- LOG "URL: $url"
- if {[dict get $data -method] == "GET"} {set token [http::geturl $url -command ::Web::httpCallback]
- } else {set token [http::geturl $url -method [dict get $data -method] -query [dict get $data -body] -command ::Web::httpCallback]}
- }
-
- }
-
-
- namespace eval Nest {
- variable Status ""
- variable StatusUndefined 0
- variable thermostats [dict create]
- variable devices [dict create thermostats "" protects ""]
-
- proc Authorize {authPIN} {
- # Sends PIN to Nest to retrieve authentication string
- set authURL [string map "|AUTHPIN| $authPIN" $::API::authorizeURL]
-
- }
-
- proc Query {args} {
- # Queries The Nest API Based on Your Parameters
- # If blank (::Nest::Query) it will query all parameters into the main dictionary
- # -thermostat <therm ID or name> -smoke <smokeCO ID or name>
- Web::Send -request Query
- }
-
- proc Set {args} {
-
- }
-
- proc SetStatus {newStatus args} {
- LOG "---- New Nest Status Received!\n$newStatus"
- if {$::Nest::Status != "" && [dict exists $::Nest::Status current]} {dict set ::Nest::Status old [dict get $::Nest::Status current]}
- dict set ::Nest::Status current $newStatus
- getDevices thermostats
- getDevices protects
- }
-
- proc getDevices {kind} {
- # Parses the Data from the Queries and returns "kind" to the proper
- # Dictionary with name/id as the key/value pairs
- # Options for Kind are: thermostats, smoke
- set tempDict [dict create]
- if {$::Nest::Status == ""} {LOG "Query Never Performed? Attempting Query..."; Nest::Query}
- switch -regexp -nocase -- $kind {
- {^thermostats?$} {
- if {[dict exists $::Nest::Status current devices thermostats]} {set thermIDs [dict keys [dict get $::Nest::Status current devices thermostats]]
- } else {LOG "No Thermostats Detected - Attempting Query"y}
- foreach id $thermIDs {
- set name [dict get $::Nest::Status current devices thermostats $id name]
- dict set tempDict $name $id
- dict set ::Nest::Devices thermostats $tempDict
- }
- }
- {^protects?} {
- if {[dict exists $::Nest::Status current devices smoke_co_alarms]} {set protectIDs [dict keys [dict get $::Nest::Status current devices smoke_co_alarms]]
- } else {LOG "No Protects Were Detected"}
- foreach id $protectIDs {
- set name [dict get $::Nest::Status current devices smoke_co_alarms $id name]
- dict set tempDict $name $id
- dict set ::Nest::Devices protects $tempDict
- }
- }
- {^all} {}
- default {LOG "Kind was not Recognized - should be thermostats, protects, or all"}
- }
- }
-
- proc nameFromID {id {kind "thermostats"}} {
- # Used to Return the Room Name of a given Device ID
- dict for {k v} [dict filter [dict get $::Nest::Devices $kind] value $id] {
- lappend keys $k
- }
- return $keys
- }
- }
-
- namespace eval URC {
- proc interfaceServer {} {
- # Interfaces with URC Interfaces
- }
- }
-
- Nest::Query;after 3000 {set i 0};vwait i