Posted to tcl by Napier at Thu Jul 03 15:56:55 GMT 2014view raw

  1. # --------------------------------------------
  2. # Nest & Smoke+CO TCL Integration Library
  3. # Connects to the Nest API for control &
  4. # monitoring of NEST Products.
  5. #
  6. # Author: Automation Apps
  7. # --------------------------------------------
  8. # Main Command Syntax:
  9. # -fanTimer <start,stop> -mode <off, heat, cool, heat-cool> -
  10. #
  11.  
  12. enableLOG 0
  13. set varIfMRXMAC "000"
  14.  
  15. # Include Packages in the includePackages List
  16. set includePackages "http tls json json::write tdom uri"
  17. foreach {pkg} $includePackages {package require $pkg}
  18.  
  19. tls::init -tls1 true
  20. http::register https 443 tls::socket
  21.  
  22. namespace eval API {
  23. # Be Sure to Set your Client Secret & Client ID
  24. variable FB_Auth "auth=c.DWjy5mnOvgHcJ1NdKisooOZEIyO5dpVacU8tC5B9Me6QtkQ7vtHOqwYMdneHz8pnNf3raKOQlVjaIVmyQCZFnAxC5H7vKGP5WK3IKTTX3l8Gd0pBB0B1th2Q5cOWZFNWOJiq8IPHZPv6U5HB"
  25. variable authState $::varIfMRXMAC
  26. variable baseURL "https://developer-api.nest.com"
  27. variable basePort 443
  28. variable client_secret "wCJzrq2VuyWcH70DjENWpjupi"
  29. variable client_id "7c3fe371-5740-48ac-a1b2-74df90145813"
  30. variable ThermBaseURL "devices/thermostats/"
  31. variable authRequestURL "https://home.nest.com/login/oauth2?client_id=7c3fe371-5740-48ac-a1b2-74df90145813&state=ip"
  32. variable authorizeURL "https://api.home.nest.com/oauth2/access_token?code=|AUTHPIN|&client_id=${client_id}&client_secret=${client_secret}&grant_type=authorization_code"
  33. variable maxEvents 25
  34. # variable forecast "https://home.nest.com/api/0.1/weather/forecast/${zip},${country}"
  35.  
  36.  
  37. namespace eval CMDS {
  38. variable FanTimer "fan_timer_active" ; # Boolean - Sets 15 Minute Fan Timer {"fan_timer_active": true }
  39. # JSON Example: { "fan_timer_active": true }
  40. variable Temp(F) "target_temperature_f" ; # Integer - Sets Fahrenheit {"target_temperature_f": 70} (1 Accuracy)
  41. # JSON Example: { "target_temperature_f": 70 }
  42. variable Temp(C) "target_temperature_c" ; # Integer - Sets Celcius (0.5 Accuracy)
  43. # JSON Example: { "target_temperature_c": 30.5 }
  44. variable HighTemp(F) "target_temperature_high_f" ; # Integer - Set High Temp in Fahrenheit (1 Accuracy)
  45. # JSON Example: { "target_temperature_high_f": 75 }
  46. variable HighTemp(C) "target_temperature_high_c" ; # Integer - Set High Temp in Celcius (0.5 Accuracy)
  47. # JSON Example: { "target_temperature_high_c": 40.5 }
  48. variable LowTemp(F) "target_temperature_low_f" ; # Integer - Set Low Temp in Fahrenheit (1 Accuracy)
  49. # JSON Example: { "target_temperature_low_f": 65 }
  50. variable LowTemp(C) "target_temperature_low_c" ; # Integer - Set Low Temp in Celcius (0.5 Accuracy)
  51. # JSON Example: { "target_temperature_low_c": 20.5 }
  52. variable mode "hvac_mode" ; # String - Set HVAC Mode <heat, cool, heat-cool, off>
  53. # JSON Example: {"hvac_mode": "heat-cool"}
  54.  
  55. variable Type 0
  56.  
  57. trace add variable ::API::CMDS::FanTimer read {::System::traceCallback i}
  58. trace add variable ::API::CMDS::Temp(F) read {::System::traceCallback i}
  59. trace add variable ::API::CMDS::Temp(C) read {::System::traceCallback i}
  60. trace add variable ::API::CMDS::HighTemp(F) read {::System::traceCallback i}
  61. trace add variable ::API::CMDS::HighTemp(C) read {::System::traceCallback i}
  62. trace add variable ::API::CMDS::LowTemp(F) read {::System::traceCallback i}
  63. trace add variable ::API::CMDS::LowTemp(C) read {::System::traceCallback i}
  64. trace add variable ::API::CMDS::mode read {::System::traceCallback s}
  65. }
  66. }
  67.  
  68. set bodyTXT ""
  69. namespace eval Web {
  70.  
  71. variable RequestData [dict create]
  72.  
  73. proc Send {args} {
  74. # Syntax:
  75. # -method $METHOD -url $URL -params $PARAMS -suffix $URL-Suffix -port $port -body $body -request $request
  76. # -request feeds the RequestData Variable to Identify to the Callback
  77. # Web::Send -method PATCH -url https://www.url.com -params "Hi Hello" -suffix "devices/hello" -body $json -port 9553 would PATCH:
  78. # https://www.url.com:9553/devices/hello?auth={AUTH CODE}&hi=hello with the value of $json as the body
  79. LOG "Beginning Web::Send $args"
  80. set tempDict $args
  81. if {$::API::FB_Auth == 0} {LOG "OAuth Required Before Continuing"; #TODO: Build Re-Auth Service}
  82. if {![dict exists $tempDict -suffix]} {LOG "No Suffix Detected"; dict set tempDict -suffix ""}
  83. if {![dict exists $tempDict -url]} {LOG "No URL using Default"; dict set tempDict -url $::API::baseURL}
  84. if {![dict exists $tempDict -method]} {LOG "No Method Defined, Using GET"; dict set tempDict -method "GET"}
  85. if {[dict exists $tempDict -params]} {LOG "Formatting Query"; dict set tempDict -params "&[::http::formatQuery {*}[dict get $tempDict -params]]"
  86. } else {LOG "No Params Detected"; dict set tempDict -params ""}
  87. if {[dict exists $tempDict -port]} {LOG "Custom Port Detected: [dict get $tempDict -port]"; http::register https [dict get $tempDict -port] tls::socket
  88. } else {http::register https $::API::basePort tls::socket}
  89. if {![dict exists $tempDict -body]} {LOG "No Body Detected"; dict set tempDict -body ""}
  90. if {![dict exists $tempDict -request]} {LOG "No Request Detected"; dict set tempDict -request "None"
  91. } elseif {[dict get $tempDict -request] == "Query"} {LOG "Set Event Stream Header"; http::config -accept "text/event-stream"
  92. } else {LOG "Set Accept All Header"; http::config -accept "/*"}
  93. set ::Web::RequestData $tempDict
  94. 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]
  95. } else {set token [http::geturl [dict get $tempDict -url]/[dict get $tempDict -suffix]?${::API::FB_Auth}[dict get $tempDict -params] \
  96. -method [dict get $tempDict -method] -query [dict get $tempDict -body] -command ::Web::httpCallback]
  97. }
  98. }
  99.  
  100. proc httpCallback {token} {
  101. #set data $::Web::RequestData
  102. set response [dict create data [http::data $token] code [http::code $token]]
  103. #LOG "----- RECEIVING DATA -----"
  104. #LOG "$response"
  105. #LOG "--------------------------"
  106. # LOG "Token: $token"
  107. if {[string match {30[1237]} [::http::ncode $token]]} {LOG "Redirect Required!"; ::Web::startRedirect $token
  108. } elseif {[::http::ncode $token] == 200} {LOG "Successfull Call Detected"
  109. #http::cleanup $token
  110. #switch -- [dict get $data -request] {
  111. # Authorize {LOG "Authorize Data Assumed"; set response [::json::json2dict [dict get $response data]]}
  112. # default {LOG "Unknown or Undefined Request, Saving Value to ::Nest::StatusUndefined"; set ::Nest::StatusUndefined $data}
  113. #}
  114. # Query {LOG "Query Data Assumed"; ::Nest::setStatus [::json::json2dict [dict get $response data]]}
  115. } elseif {[::http::ncode $token] == 429} {LOG "!!!! ---- |||| Too Many Requests Error Received |||| ----- !!!!"}
  116. }
  117.  
  118. proc httpProgress {token total current} {
  119. LOG "Start Progress"; LOG "Total: $total"; LOG "Current: $current"
  120. # The Following if Statement is used to parse out the random "e" received
  121. # at the start of the Event Stream Data.
  122. if {$current == 1} {LOG "END"; return}
  123. upvar #0 $token state
  124. set body "$state(body)"
  125. set occurrences [regsub -all {\s*\n\s*\n(\s*\n)*} $body "\n" body]
  126. set body [split $body \n]
  127. if {[dict get [lindex $body 0] "event:"] == "auth_revoked"} {::Nest::AuthRevoked}
  128. # The occurrences variable actively tracks how many events are currently saved
  129. # in the $state(body) variable. It is likely a good idea to flush this out at
  130. # a set interval by closing (http::cleanup) and re-initiating the event.
  131. LOG "Event Occurences: $occurrences"
  132. if {$occurrences >= $::API::maxEvents} {LOG "Flushing Events"
  133. # Process to Flush Events & Re-Initiate the Event Stream
  134. # should go here.
  135. }
  136. if {$::Nest::Status == ""} {
  137. set startData [::System::convertJSON [lindex $body 1]]
  138. LOG "Start Data:\n$startData"
  139. ::Nest::setStatus [dict get $startData data]
  140. } else {
  141. set newData [::System::convertJSON [lindex $body end-1]]
  142. if {$newData != ""} {
  143. LOG "New Data:\n$newData"
  144. if {[dict exists $::Nest::Status current] && [dict get $::Nest::Status current] != [dict get $newData data]} {
  145. LOG "Data is Different, Setting Nest Status Variable"; ::Nest::setStatus [dict get $newData data]
  146. } else {LOG "New Data has Not Changed, Do Nothing"}
  147. }
  148. }
  149. }
  150.  
  151. proc startRedirect {token} {
  152. set data $::Web::RequestData
  153. array set meta [set ${token}(meta)]
  154. LOG "[parray {*}$token]"
  155. http::cleanup $token
  156. if {![info exist meta(Location)]} {LOG "No Redirection Indicator?"; return}
  157. array set uri [::uri::split $meta(Location)]
  158. LOG "URI:"; LOG "[parray uri]"
  159. unset meta
  160. if {$uri(host) == ""} {set uri(host) $uri(host)}
  161. set url [eval ::uri::join [array get uri]]
  162. LOG "URL: $url"
  163. http::config -accept "text/event-stream"
  164. if {[dict get $data -method] == "GET"} {set token [http::geturl $url -keepalive 1 -progress ::Web::httpProgress]
  165. } else {set token [http::geturl $url -method [dict get $data -method] -query [dict get $data -body] -command ::Web::httpCallback]}
  166. }
  167.  
  168. proc sendEmail {to msg args} {
  169. # This Procedure will Prepare and Send an E-Mail to the address(es) provided in
  170. # the "to" argument. It will include the content in the "msg" argument.
  171. # Additionally the following arguments can be defined if needed:
  172. # -footer $txt
  173.  
  174. }
  175. }
  176.  
  177. namespace eval Nest {
  178. variable Status ""
  179. variable StatusUndefined 0
  180. variable thermostats [dict create]
  181. variable devices [dict create thermostats "" protects "" structures ""]
  182. variable presence [dict create]
  183.  
  184.  
  185. proc Authorize {authPIN} {
  186. # Sends PIN to Nest to retrieve authentication string
  187. set authURL [string map "|AUTHPIN| $authPIN" $::API::authorizeURL]
  188. LOG "Authorization URL:\n $authURL"
  189. Web::Send -method POST -url $authURL -request Authorize
  190. #TODO: Send the Authorization as POST with Parameters
  191. }
  192.  
  193. proc AuthRevoked {args} {
  194. # This Procedure is called when the "Auth_Revoked" Event
  195. # is Received from Nest. You will need to re-authenticate
  196. # and get a new "Auth Token" when this occurs.
  197. }
  198.  
  199. proc Query {args} {
  200. # Queries The Nest API Based on Your Parameters
  201. # If blank (::Nest::Query) it will query all parameters into the main dictionary
  202. # Do NOT do this Often as Nest limits the amount of calls you can do per
  203. # month per token.
  204. # All data will be kept up-to-date using the Rest Streaming Feature of the API.
  205. # -thermostat <therm ID or name> -smoke <smokeCO ID or name>
  206. Web::Send -request "Query"
  207. }
  208.  
  209. proc Set {args} {
  210. # This Procedure allows you to Control Nest Products using
  211. # their Official API.
  212. # -command - Specify the command as a key/value pair. Comaand options and
  213. # examples can be found in the ::API::CMDS Namespace and should be referenced
  214. # as that variable.
  215. # -value - Specify the Value you would like to feed to the Process. This should follow
  216. # the examples in the "::API::CMDS" namespace.
  217. # -device {deviceID} - This argument specifies the device's ID which
  218. # should be manipulated
  219. # -kind {kind} - Specify what kind of device will be controlled.
  220. # this is an optional value but specifying it will increase overall speed.
  221. # Syntax Example:
  222. # ::Nest::Set $::API::CMDS::Temp(F) 75 -device $thermID -kind thermostat
  223.  
  224. LOG "Beginning Set Procedure"
  225. if {[dict exists $args -kind]} {
  226. LOG "Kind was Specified: [dict get $args -kind]"
  227. switch -regexp -- [dict get $args -kind] {
  228. {^thermostats?$|^therms?$} {::Nest::setThermostat [dict get $args -command] [dict get $args -value] [dict get $args -device]}
  229. {^protects?$|^smokecos?$|^detectors?$} {LOG "Nest Protect Specified\nThere are currently No Official Set Options for Nest Protect"}
  230. default {puts "No"}
  231. }
  232. } else {
  233. LOG "Kind was Not Specified - Starting ID Trace"
  234. }
  235. }
  236.  
  237. proc setThermostat {cmd value id} {
  238. LOG "Setting the Thermostat with ID: $id"
  239. # This Procedure should generally not be called directly, use ::Nest::Set $cmd $value -kind (optional)
  240. # Currently Adds JSON and sends one Command at a Time - another procedure will be written to send multiple
  241. # values with a single call.
  242.  
  243. if {$::API::CMDS::Type == "i"} { set json [::json::write object $cmd $value]
  244. } elseif {$::API::CMDS::Type == "s"} { set json [::json::write object $cmd [::json::write string $value]]
  245. }
  246. LOG "|--- Sending in Patch Body:\n$json"
  247. LOG "|--- Sending to Suffix:\n${::API::baseURL}/${::API::ThermBaseURL}$id"
  248. ::Web::Send -method PATCH -suffix ${::API::ThermBaseURL}$id -body $json
  249. }
  250.  
  251. proc Experimental {args} {
  252. # This procedure will house non-official API procedures that
  253. # may not be supported in the long-term. Generally this will
  254. # emulate features used on the Official Nest App & Web Control
  255. # app.
  256.  
  257. }
  258.  
  259. proc setStatus {newStatus args} {
  260. # This Procedure handles Status Setting when a Query is Made. If Status was already present, it will save the
  261. # "current" Status into the "old" Status Dictionary and the newly received status into the "current" dictionary.
  262. # This can then be used to evaluate events as needed for Macro Integration
  263.  
  264. LOG "---- New Nest Status Received!\n$newStatus"
  265. if {$::Nest::Status != "" && [dict exists $::Nest::Status current]} {
  266. dict set ::Nest::Status old [dict get $::Nest::Status current]
  267. }
  268. dict set ::Nest::Status current $newStatus
  269. getDevices all
  270. }
  271.  
  272. proc getDevices {kind} {
  273. # Parses the Data from the Queries and returns "kind" to the proper
  274. # Dictionary with name/id as the key/value pairs
  275. # Options for Kind are: thermostats, smoke
  276.  
  277. set tempDict [dict create]
  278. LOG "Current Status Var Value"
  279. if {$::Nest::Status == ""} {LOG "Query Never Performed? Attempting Query..."; Nest::Query}
  280. switch -regexp -nocase -- $kind {
  281. {^thermostats?$} {
  282. LOG "Getting Thermostats"
  283. if {[dict exists $::Nest::Status current devices thermostats]} {set thermIDs [dict keys [dict get $::Nest::Status current devices thermostats]]
  284. } else {LOG "No Thermostats Detected"; return -1}
  285. LOG "Therm IDs: $thermIDs"
  286. foreach id $thermIDs {
  287. set name [dict get $::Nest::Status current devices thermostats $id name]
  288. dict set tempDict $name $id
  289. }
  290. if {[dict exists $::Nest::devices thermostats] && [dict get $::Nest::devices thermostats] == $tempDict} {LOG "No Change to Thermostats, Do Nothing"; return}
  291. LOG "Change Detected to Thermostats, Setting Devices Dictionary"
  292. dict set ::Nest::devices thermostats $tempDict
  293. return
  294. }
  295. {^protects?$} {
  296. LOG "Getting Protects"
  297. if {[dict exists $::Nest::Status current devices smoke_co_alarms]} {set protectIDs [dict keys [dict get $::Nest::Status current devices smoke_co_alarms]]
  298. } else {LOG "No Protects Were Detected"; return -1}
  299. foreach id $protectIDs {
  300. set name [dict get $::Nest::Status current devices smoke_co_alarms $id name]
  301. dict set tempDict $name $id
  302. }
  303. if {[dict exists $::Nest::devices protects] && [dict get $::Nest::devices protects] == $tempDict} {LOG "No Change to Protects, Do Nothing"; return}
  304. LOG "Change Detected to Protects, Setting Devices Dictionary"
  305. dict set ::Nest::devices protects $tempDict
  306. return
  307. }
  308. {^structures?$|^homes?$|^buildings?$} {
  309. LOG "Getting Structures"
  310. if {[dict exists $::Nest::Status current structures]} {set structIDs [dict keys [dict get $::Nest::Status current structures]]
  311. } else {LOG "No Structured Were Detected"; return -1}
  312. foreach id $structIDs {
  313. set name [dict get $::Nest::Status current structures $id name]; LOG "Structure Name: $name"
  314. if {[dict exists $::Nest::Status current structures $id thermostats]} {set therms [dict get $::Nest::Status current structures $id thermostats]
  315. } else {LOG "No Thermostats for Structure $name"; set therms ""}
  316. if {[dict exists $::Nest::Status current structures $id smoke_co_alarms]} {set protects [dict get $::Nest::Status current structures $id smoke_co_alarms]
  317. } else {LOG "No Protects for Structure: $name"; set protects ""}
  318. # Check Presence Status for Structures and Report to the System if it has changed
  319. dict set tempDict $name [dict create id $id thermostats $therms protects $protects]
  320. set presence [dict get $::Nest::Status current structures $id away]
  321. if {![dict exists $::Nest::presence $id]} {LOG "Creating Presence for First Time"; dict set ::Nest::presence $id [dict create status $presence startup 1]
  322. } elseif {[dict exists $::Nest::presence $id] && [dict get $::Nest::presence $id status] != $presence} {LOG "Presence Changed"; dict with ::Nest::presence $id {set status $presence; set startup 0}}
  323. }
  324. if {[dict exists $::Nest::devices structures] && [dict get $::Nest::devices structures] == $tempDict} {LOG "No Change to Structures, Do Nothing"; return}
  325. LOG "Change Detected to Structures, Setting Devices Dictionary"
  326. dict set ::Nest::devices structures $tempDict
  327. return
  328. }
  329. {^all} {LOG "Getting All Devices & Structures"
  330. getDevices thermostats; getDevices protects; getDevices structures
  331. }
  332. default {LOG "Kind was not Recognized - should be thermostats, protects, or all"}
  333. }
  334. }
  335.  
  336. proc nameFromID {id {kind "thermostats"}} {
  337. # Used to Return the "Room Name" or "Structure Name" of a given "ID"
  338.  
  339. if {$kind != "structures"} {
  340. LOG "Parsing ID"
  341. dict for {k v} [dict filter [dict get $::Nest::devices $kind] value $id] {
  342. set keys $k
  343. }
  344. } else {
  345. LOG "Getting Structure Name"
  346. set structNames [dict keys [dict get $::Nest::devices structures]]
  347. foreach struct $structNames {
  348. if {$id == [dict get $::Nest::devices structures $struct id]} {
  349. set keys $struct
  350. } else {continue}
  351. }
  352. }
  353. LOG "Returning: $keys"
  354. return $keys
  355. }
  356.  
  357. proc DeviceNamesByStructure {struct {kind "thermostats"}} {
  358. # Will return a list with the thermostat names for a given Structure.
  359. # Structure can be the Structure ID or Structure Name.
  360. set names ""
  361. if {![dict exists $::Nest::devices structures $struct $kind]} {
  362. LOG "Getting Name from Structure ID"; set struct [::Nest::nameFromID $struct structures]
  363. }
  364. foreach deviceID [dict get $::Nest::devices structures $struct $kind] {lappend names [::Nest::nameFromID $deviceID $kind]}
  365. LOG "$kind in Room: $names"
  366. return $names
  367. }
  368.  
  369. proc listRooms {{struct "all"}} {
  370. # This procedure will return the rooms within the project which includes both
  371. # the Thermostats & the Nest Protects. Returns into List which should be useable
  372. # for URC's TCL List Objects. Automatically handles duplicate values and only returns
  373. # unique values.
  374. # By default listRooms will list all rooms in the System regardless of what Nest
  375. # structure they are within. Providing a Structure Name or ID as an argument will
  376. # list rooms only for that structure.
  377. # This differentiates itself from ::Nest::DeviceNamesByStructure because it returns
  378. # a unique list of rooms. It also automatically checks all room names that any
  379. # device is placed into.
  380. # Syntax:
  381. # ::Nest::listRooms "Home"
  382.  
  383. set l ""
  384. if {$struct == "all"} {
  385. set l [dict keys [dict get $::Nest::devices thermostats]]
  386. lappend l {*}[dict keys [dict get $::Nest::devices protects]]
  387. } else {
  388. if {![dict exists $::Nest::devices structures $struct]} {
  389. LOG "Getting Name from Structure ID"; set struct [::Nest::nameFromID $struct structures]
  390. }
  391. foreach deviceID [dict get $::Nest::devices structures $struct thermostats] {lappend l [::Nest::nameFromID $deviceID thermostats]}
  392. foreach deviceID [dict get $::Nest::devices structures $struct protects] {lappend l [::Nest::nameFromID $deviceID protects]}
  393. }
  394. LOG "Raw List is:\n$l"
  395. set l [lsort -unique $l]
  396. LOG "Formatted List is:\n$l"
  397. return $l
  398. }
  399.  
  400. proc findDeviceKind {id} {
  401. # This Procedure will return the "Kind" of Device based on a specified ID
  402. # Example: ::Nest::findDeviceKind _MXPPHwK669HMC6Mtqb51qtN3uLTVu2B
  403. # Returns: < thermostats , protects, structures >
  404. # You can then call ::Nest::nameFromID $response to get the name/room/structure.
  405. # set name [::Nest::nameFromID $id [::Nest::findDeviceKind $id]]
  406.  
  407. set l [dict values [dict get $::Nest::devices thermostats]]
  408. if {[string match *$id* $l]} {return thermostats}
  409. set l [dict values [dict get $::Nest::devices protects]]
  410. if {[string match *$id* $l]} {return protects}
  411. set l ""
  412. set structNames [dict keys [dict get $::Nest::devices structures]]
  413. foreach struct $structNames {
  414. lappend l {*}[dict get $::Nest::devices structures $struct id]
  415. }
  416. if {[string match *$id* $l]} {return structures}
  417. }
  418.  
  419. # Trace the Devices Variable to trigger changes to UI when new devices are added or changed
  420. # in any way.
  421. trace add variable ::Nest::devices write {::System::traceCallback devices}
  422. trace add variable ::Nest::Status write {::System::traceCallback status}
  423. trace add variable ::Nest::presence write {::System::traceCallback presence}
  424. }
  425.  
  426. namespace eval URC {
  427.  
  428. proc UpdateUI {eventType} {
  429. # This Procedure is called when a setting needs to be
  430. # updated to URC User Interfaces.
  431. # eventType = <devices, status>
  432.  
  433. }
  434.  
  435. proc onPresenceChange {id status} {
  436. # Called when Presence change is considered valid.
  437. # Will include the Structure ID of the Status Change
  438. # as well as the status.
  439. # It is safe to Execute Event at this point.
  440. # Status Will be Either: <away, home>
  441.  
  442. LOG "Presence Changed for ID: $id\nChanged to: $status"
  443.  
  444. }
  445.  
  446. proc interfaceServer {} {
  447. # Interfaces with URC Interfaces
  448. }
  449. LOG "-- URC Module Information --"
  450. # LOG "Module Version: $::varIfVersionNum"
  451. LOG "Created for: Nest API, Firmware v4.1"
  452.  
  453. }
  454.  
  455. namespace eval System {
  456.  
  457. proc traceCallback {value args} {
  458. LOG "Trace Callback Initiated"
  459. LOG "Value: $value"
  460. LOG "Args:\n$args"
  461.  
  462. switch -- $value {
  463. status {
  464. LOG "Nest Status has been Updated!"
  465. ::URC::UpdateUI status
  466. }
  467. i {set ::API::CMDS::Type i}
  468. s {set ::API::CMDS::Type s}
  469. devices {
  470. LOG "|---- NEW Devices Variable Value:\n$::Nest::devices"
  471. ::URC::UpdateUI devices
  472. }
  473. presence {
  474. LOG "|---- Presence Change Detected"
  475. dict for {id val} $::Nest::presence {
  476. if {[dict get $::Nest::presence $id startup] == 0} {
  477. LOG "Presence Change Validated"
  478. dict set ::Nest::presence $id startup -1
  479. ::URC::onPresenceChange $id [dict get $::Nest::presence $id status]
  480. }
  481. }
  482.  
  483.  
  484. }
  485. default {LOG "ERROR in traceCallback"}
  486. }
  487. }
  488.  
  489. proc convertJSON {data} {
  490. LOG "|--- Convert JSON Starts"
  491. set newData [string map {"data: " ""} $data]
  492. if {$newData == "null"} {LOG "Heartbeat Detected, Ending Parse"; return}
  493. # set newData [split $data " "]
  494. set newData [json::json2dict $newData]
  495. return $newData
  496. }
  497. }
  498.  
  499. vwait forever