While Nest Labs hasn’t released a formal (documented & supported) API, I thought
                                    I’d do a bit of digging to see how they’re using the network and what might be
                                    achievable.
                                
                                A few things are going on, the majority as you’d probably expect.
                                
                                    The web interface is using a long polling technique apparently to watch for updates
                                    to the schedule, temperature, etc.
                                
                                
                                     
                                
                                
                                    I haven’t determined what the frequency is though, or the wait time. It’s very
                                    inconsistent, even when I wouldn’t expect much new “live” data to be available on
                                    the network, it frequently updates and polls again.
                                
                                There are a few constants set in the HOME page script:
                                C.ABSENT_USER_THRESHOLD     = +('300') || 0;  // seconds
C.DEAD_DEVICE_THRESHOLD     = +('300') || 0;  // seconds
C.pollingInterval           = +('2500') || 0;       // ms
C.WEATHER_POLLING_INTERVAL  = +('120000') || 0; // ms
                                 
                                
                                    If the C.pollingInterval value were for the subscribe endpoint mentioned above, I’d
                                    see a LOT more calls than I do – so I’m still not clear how the polling interval is
                                    decided.
                                
                                The API calls, for the most part are using JSONP syntax over an HTTPS connection.
                                
                                    The most frequent request is to “subscribe.” It sends as part of the GET request a
                                    large block of encoded JSON (using encodeURIComponent and then JSON.stringify).
                                
                                
                                    I’m not familiar with the key/value system that they’re using (it may just be
                                    something they’ve constructed in-house – although given the number of
                                    open source JavaScript libraries they’re
                                    using, I thought someone might recognize it):
                                
                                “key”, “{actualkey}.{value}”
                                
                                    I don’t understand why they’ve redundantly specified “key” in a list of keys when
                                    it’s evident that the actual key is contained within the value as a
                                    delimited string. It’s more data to send and more data to parse this way. So, again,
                                    maybe it’s based on some DB or model system I’m not familiar with. (Anyone recognize
                                    it?)
                                
                                
                                    I’ve substituted the actual values (as they are serial numbers of my devices) with
                                    text representations of what the value represented below:
                                
                                {"keys":
    [{"key":"user.#USERID#",
        "version":209478897,"timestamp":1324159145000},
    {"key":"user_alert_dialog.#USERID#",
        "version":-1320296685,"timestamp":1325967612000},
    {"key":"structure.#STRUCTURE-GUID#",
        "version":656192675,"timestamp":1325967612000},
    {"key":"device.#DEVICE 1 SERIAL NUMBER#",
        "version":1485027516,"timestamp":1326034984000},
    {"key":"shared.#DEVICE 1 SERIAL NUMBER#",
        "version":588844038,"timestamp":1326034818000},
    {"key":"schedule.#DEVICE 1 SERIAL NUMBER#",
        "version":1187107985,"timestamp":1326005677000},
    {"key":"track.#DEVICE 1 SERIAL NUMBER#",
        "timestamp":1326035650601,"version":1041047847},
    {"key":"device.#DEVICE 2 SERIAL NUMBER#",
        "version":149169270,"timestamp":1326034820000},
    {"key":"shared.#DEVICE 2 SERIAL NUMBER#",
        "version":659841570,"timestamp":1326034820000},
    {"key":"schedule.#DEVICE 2 SERIAL NUMBER#",
        "version":-2016290692,"timestamp":1326005625000},
    {"key":"track.#DEVICE 2 SERIAL NUMBER#",
        "timestamp":1326035650862,"version":528978433},
    {"key":"device.#DEVICE 3 SERIAL NUMBER#",
        "version":1637112547,"timestamp":1326035399000},
    {"key":"shared.#DEVICE 3 SERIAL NUMBER#",
        "version":760504326,"timestamp":1326035397000},
    {"key":"schedule.#DEVICE 3 SERIAL NUMBER#",
        "version":-314552357,"timestamp":1326003402000},
    {"key":"track.#DEVICE 3 SERIAL NUMBER#",
        "version":-645931164,"timestamp":1326035531802}]}"
                                
                                    We’ve got three thermostats, so there are always three sets of subscription requests
                                    for each call to subscribe.
                                
                                
                                    Using my iPad, I adjusted the set point for our second story (#DEVICE 2#) down one
                                    degree Fahrenheit (to 67°).
                                
                                
                                    Within approximately a second, the most recent pending
                                    subscribe request returned with a far more interesting payload:
                                
                                jQuery17108417355176061392_1326035646750(
    { "status": 200,
        "headers": {
            "X-nl-skv-key": "shared.#DEVICE 2 SERIAL NUMBER#",
            "X-nl-skv-version": 869022424,
            "X-nl-skv-timestamp": 1326038279000,
            "X-nl-service-timestamp": 1326038279825
        },
        "payload": {
            "current_temperature": 19.98,
            "hvac_fan_state": false,
            "name": "TWO", "hvac_heat_x2_state": false,
            "hvac_ac_state": false,
            "can_cool": true,
            "auto_away": 0,
            "compressor_lockout_enabled": false,
            "target_temperature_low": 16.66667,
            "target_temperature_high": 26.66667,
            "compressor_lockout_timeout": 0,
            "hvac_heater_state": false,
            "hvac_aux_heater_state": false,
            "target_temperature": 19.44444,
            "can_heat": true,
            "target_temperature_type": "heat",
            "target_change_pending": true
        }
    });
                                
                                    Everything above is needed to update the current state of the UI. As you can see,
                                    the current temperature (returned as Celsius apparently) is 19.98 (67.964°F). The
                                    current temperature as displayed on the thermostat and the web UI was 68.
                                
                                
                                    Seeing these return values makes me think that they may be using Ruby and Rails
                                        (as the naming convention tends to follow Rails naming using underscores between
                                        words). I know for example, I wouldn’t name variables/columns that way when
                                        building a C#/JavaScript MVC project.
                                
                                
                                    Rather than just a delta payload of what’s changed, they’ve currently opted for
                                        a full update of all information related to the thermostat state.
                                    
                                
                                
                                    Several seconds later, a much larger payload was returned to a
                                        subscribe request:
                                
                                "status": 200,
"headers": {
    "X-nl-skv-key": "device.#DEVICE 2 SERIAL NUMBER#",
    "X-nl-skv-version": -2086438581,
    "X-nl-skv-timestamp": 1326038378000,
    "X-nl-service-timestamp": 1326038379023
},
"payload": {
    "ob_orientation": "O",
    "upper_safety_temp": 1000.0,
    "forced_air": true,
    "creation_time": 1324142042019,
    "switch_preconditioning_control": false,
    "click_sound": "on",
    "leaf": false, "user_brightness": "auto",
    "learning_state": "steady",
    "heat_pump_comp_threshold": -1000.0,
    "local_ip": "10.0.0.205",
    "backplate_serial_number": "#SHOULD BE DEVICE 2 SERIAL NUMBER, BUT ISN'T?#",
    "capability_level": 1.03,
    "postal_code": "#POSTALCODE#",
    "upper_safety_temp_enabled": false,
    "heat_pump_aux_threshold": 10.0,
    "lower_safety_temp_enabled": true,
    "serial_number": "#DEVICE 2 SERIAL NUMBER#",
    "temperature_lock": false,
    "learning_time": 1002,
    "current_version": "1.0.4",
    "model_version": "Diamond-1.10",
    "backplate_bsl_info": "BSL",
    "auto_away_enable": true,
    "heat_pump_comp_threshold_enabled": false,
    "fan_mode": "auto",
    "range_enable": false,
    "temperature_scale": "F",
    "backplate_mono_info": "TFE (BP_DVT) 3.5.2 (ehs@ubuntu) 2011-11-05 12:00:00",
    "backplate_bsl_version": "1.1",
    "equipment_type": "gas",
    "range_mode": false,
    "lower_safety_temp": 7.0,
    "has_fan": true,
    "hvac_wires": "Heat,Cool,Fan,Common Wire,Rc",
    "learning_mode": true,
    "away_temperature_high": 32.0,
    "switch_system_off": false,
    "time_to_target": 1326039444,
    "away_temperature_low": 14.444444444444445,
    "current_humidity": 45,
    "mac_address": "#MACADDR#",
    "backplate_mono_version": "3.5.2",
    "has_aux_heat": false,
    "type": "TBD",
    "hvac_pins": "W1,Y1,C,Rc,G",
    "has_heat_pump": false,
    "heat_pump_aux_threshold_enabled": true,
    "battery_level": 3.945,
    "target_time_confidence": 1.0
}
                                 
                                A few things to note:
                                
                                    - 
                                        Upper_safety_temperature is just a bit beyond my comfort zone
                                        at 1832°F. I don’t know why it’s sending a value like that to the client, and
                                        why it’s stupidly high.
                                    
- 
                                        The backplate serial number doesn’t match with the thermostat according to the
                                        payload response. I don’t know why this might be as I confirmed that the numbers
                                        matched through visual inspection of the device just now.
                                    
- 
                                        The majority of these details are exposed in one way or another in the details
                                        area of the web UI.
                                    
- 
                                        Time to target (payload.time_to_target) is unusual in that it’s a JavaScript
                                        Date value, divided by 1000. So, in the example above, the time to target is:
                                        new Date(1326039444 * 1000).toString() = >”Sun Jan 08 2012
                                            10:17:24 GMT-0600 (Central Standard Time)”Next, a payload is returned with the new status:
                                        "status": 200,
"headers": {
    "X-nl-skv-key": "shared.#DEVICE 2 SERIAL NUMBER#",
    "X-nl-skv-version": 1689916148,
    "X-nl-skv-timestamp": 1326038378000,
    "X-nl-service-timestamp": 1326038379151
},
"payload": {
    "hvac_fan_state": false,
    "name": "TWO",
    "hvac_heat_x2_state": false,
    "hvac_ac_state": false,
    "can_cool": true,
    "auto_away": 0,
    "compressor_lockout_enabled": false,
    "target_temperature_low": 16.66667,
    "current_temperature": 19.53,
    "target_temperature_high": 26.66667,
    "compressor_lockout_timeout": 0,
    "target_change_pending": false,
    "hvac_aux_heater_state": false,
    "target_temperature": 20.55556,
    "can_heat": true,
    "target_temperature_type": "heat",
    "hvac_heater_state": true
}  
                                            Here, the hvac_heater_state is set to
                                            true. The furnace is on.
                                         A little while later, that value is set to false. 
                                            Occasionally, the payload includes the complete schedule for the thermostat.
                                            I’m not going to reproduce the entire payload here as it’s too large, and
                                            quite boring. Here’s a snippet of what it returns:
                                         "schedule": {
    "#DEVICE 2 SERIAL NUMBER#": {
        "$version": 1187107985,
        "$timestamp": 1326005677000,
        "name": "One Current Schedule",
        "days": {
            "0": {
                "0": {
                    "type": "HEAT",
                    "temp": 14.445,
                    "time": 0,
                    "entry_type": "continuation"
                },
                "1": {
                    "type": "HEAT",
                    "temp": 14.445,
                    "time": 27900,
                    "entry_type": "setpoint"
                },
                "2": {
                    "type": "HEAT",
                    "temp": 20.556,
                    "time": 63000,
                    "entry_type": "setpoint"
                },
                "3": {
                    "type": "HEAT",
                    "temp": 14.445,
                    "time": 70200,
                    "entry_type": "setpoint"
                }
            },
            "1": {
                "0": {
                    "type": "HEAT",
                    "temp": 14.445,
                    "time": 0,
                    "entry_type": "continuation"
                },
                "1": {
                    "type": "HEAT",
                    "temp": 18.889,
                    "time": 20700,
                    "entry_type": "setpoint"
                },  
                                            It’s a basic table structure. The first set point of the day is at 0, and is
                                            a “continuation.” These don’t show up in the UI.
                                         Here’s what the day 1 looks like on the Nest thermostat UI: 
                                              
 
                                            When changing a temperature setpoint, I’m a bit disappointed to see that the
                                            entire schedule is sent with every request apparently. I just wouldn’t have
                                            expected that given that the more setpoints that there are, the bigger the
                                            payload must be. The UI is often sluggish when rapidly making adjustments in
                                            the schedule, and this could be one of the factors.
                                         
                                            In the example below (which I’ve snipped most of the payload sent again as a
                                            JSONP request), I’ve set the first set point to 57F.
                                             "payload": {
        "days": {
            "0": {
                "0": {
                    "type": "HEAT",
                    "temp": 14.685,
                    "time": 0,
                    "entry_type": "continuation"
                },
                "1": {
                    "type": "HEAT",
                    "temp": 15.000444444444444,
                    "time": 24300,
                    "entry_type": "setpoint"
                },
                                            For the JSONP requests sent as “MAKE CHANGE” (easily could have been PUT),
                                            they each contained the following attributes as shown below. All JSONP
                                            requests are apparently routed on the web server using “headers” rather than
                                            a RESTful URL based system:
                                             },
    "headers": {
        "X-nl-client-timestamp": 1326041210566,
        "X-nl-session-id": "#SESSION ID#",
        "X-nl-protocol-version": 1,
        "Authorization": "Basic #BASIC AUTH#"
    },
    "path": "/v1/put/schedule.#DEVICE 2 SERIAL NUMBER#",
    "redir": "https://home.nest.com/post_jsonp",
    "jsonp": "4_"
}
                                            It’s RESTful in spirit as there is a route (“path”), but it’s managed by
                                            some internal routing engine. (Now, I think that they’re not using Ruby and
                                            Rails).
                                         
                                            For something simple, like changing the current temperature of a thermostat,
                                            the request is thankfully simple:
                                         {
    "payload": {
        "shared": {
            "#DEVICE 2 SERIAL NUMBER#": {
                "target_temperature": 18.333333333333336
            }
        }
    },
    "headers": {
        "X-nl-client-timestamp": 1326041744556,
        "X-nl-session-id": "#SESSION ID#",
        "X-nl-protocol-version": 1,
        "Authorization": "Basic #BASIC AUTH#"
    },
    "path": "/v1/put",
    "redir": "https://home.nest.com/post_jsonp",
    "jsonp": "14_"
}
                                            While, I haven’t taken the time to try to write a custom UI for this
                                            undocumented API yet, it looks like it should be relatively easy to do,
                                            especially as it relates to the schedule and current temperature settings. I
                                            know there’s been some Siri proxy stuff that’s been written – but I don’t
                                            have any interest in trying to get that to work.
                                         
                                            As with most APIs like this, the trick is often getting authorization
                                            correct. For Nest, it appears that making a POST request to
                                            https://home.nest.com/accounts/login/ with
                                            username and password as form data, that
                                            the server responds with 2 cookies:
                                         
                                            - sessionid == used in X-nl-session-id in headers
- 
                                                cztoken == used as the Authorization in headers (prepended with the text
                                                “Basic “
                                            
 
                                            FYI: I also have a Node version
                                            of the API that is more up to date than this.