AV Club / Our home setup 2012

I’ve done a lot of reworking of our computers and network recently and thought I’d post some details about the various physical and digital parts that make our humble WiredPrairie home work.

Using Google Docs, I created a labeled diagram with most of the moving parts of our house (WOW, Google Docs Drawing is an amazingly decent vector drawing editor!). I’ve intentionally left off some of the noise including PCs and laptops, and a few unloved devices.

Home Setup

  • A) DSL modem. The connection to the Internet. We’ve had a flaky Internet connection over the years and have had to replace our DSL modem multiple times, but as our Internet service includes unlimited service and repairs, it’s not been a huge issue. We live in a rural area and our only choice is DSL (or expensive satellite service). It’s a 2 line modem to help improve reliability and upload bandwidth.
  • B) An Intel D2500CCE Atom N2500 Dual LAN Industrial Mini PC purchased from MitxPC. I added to it a Crucial m4 64GB SSD. This PC, with no moving parts, runs a copy of pfSense. pfSense is a tailored copy of FreeBSD for use as a firewall and router.

    I’m running an IPSec and OpenVPN server on the little box so that we can VPN from a PC or Mac and our iOS devices from anywhere. pfSense is reasonably well documented, so it’s pretty easy to get around. While it’s not as easy as some open source router replacement software (like TomatoUSB for example), it’s far more capable and extensible. The community seems great too (although I’ve not had to directly use it). My only disappointment is that the traffic graph is lame (compared to others I’ve used):
    image

  • C) Synaccess netBooter. Amazing little device for a reasonable amount of money. The single job of this device (for us), is to monitor the Internet connection. If the internet isn’t accessible over a period of 3 minutes (checked by pinging Google’s DNS servers every 30 seconds), the attached device is rebooted (I have it configured to turn the DSL modem off for 45 seconds and then restart it). Given our connection isn’t always reliable and a good kick in the [modem] seems to usually stabilize the connection, this has been a great addition to our house. The modem is buried away in the basement in an unfinished area, so this device makes it so I don’t need to worry about manually rebooting the modem anymore. It keeps a handy log to show reboot cycles.
    image
  • D) There are multiple wireless access points of various makes and models around the house. I recently bought the ASUS RT-N16 and updated it to one of the various flavors of the Tomato firmware (a very recent one designed specifically for this router). Our house is spread horizontally and over three floors, so we need multiple routers to get reasonably consistent WiFi coverage. I’ve got an access point on each floor currently.
  • E) UPS – CyberPower CP850PFCLCD – it keeps the primary systems of the house alive during a power outage/flicker event. The server below is connected via USB so that it will automatically shut down after an extended power failure. I haven’t had any troubles with the device other than turning off the constant beeping when the power actually goes out for more than a few seconds. (YES! I know the power is out, thanks for the [BEEEEEP]).
  • F) Two D-Link 24-port rack mountable Gigabit Switch DGS-1024D. Seriously, our house is way over-wired. We pulled four CAT-5e cables to each major room in our house. They all converge on one place in the unfinished area of our basement to these two switches.
  • G) HP ProLiant N40L Ultra MicroServer: Originally, this was running Windows Home Server 2011. But, just recently, I made the switch to running Windows 8 Pro. I’ve added a second network card and a USB 3.0 card (which required a 90 degree right angle molex connector for power!). I must say that I really like using Windows 8 on this box. It seems faster than WHS 2011, plus it has the new Storage Pool functionality, plus can act as a File History destination. File History really is a better file backup than the backups provided by WHS 2011 anyway.

    I had no problems with the install (as this micro server isn’t headless). It works really well and with the exception of the full image backups from WHS 2011, I’m not missing any functionality.

    I’m using no-ip.com as a replacement for the Windows Home Server dynamic IP functionality (as part of a WHS, you were allowed one dynamic IP host name such as wickedlycoolstuffatmyhouse.homeserver.com). I’d definitely recommend no-ip.com (they’re free for basic services and cheap for an improved service). The agent used by no-ip.com can be run as a Windows service and updates every 5 minutes (so that your IP address, if it changes will be reflected nearly instantly by no-ip.com. I used the WHS remote file access feature so few times I won’t miss that (it was so SLOW!). Now, with the VPN via pfSense, I can remotely wake a PC, and RDP to it directly (or access file shares). Much better.

    I’ve got a number of external HDs that I’ve got set up with scheduled tasks to back-up the server and other files (and I’m also running Storage Pools with mirroring).

  • H) From Cable Electronics (celabs), we have a video/audio distribution unit. I can’t find the model we have, as it’s 6 years old, but it’s served us so well! While we don’t have as many inputs as we had a few years ago, it now is sending the Joey video signal around the house very conveniently. We have this so we only need to buy a single extra satellite receiver rather than one for each TV.
  • I) This is a Dish Network Joey. It’s connected to the Russound and to the Audio/Video Distribution device. The remote control is RF so it works nearly anywhere in the house, and the video/audio is available on most of our TVs. We don’t really like it (as it’s not easy to use), but we like the channels …, and love having a DVR. Our setup allows the audio of the Joey (via the Russound below) to be sent to any room with built in speakers. In our kitchen, we can listen to TV with the built in room speakers rather than via the tiny built in speakers on the TV cranked.
  • J) Russound C-Series Multizone Controller – this sends audio (and remote IR signals) to 6 rooms in our house. We use this several times a day so we’re really glad we have it. We’ve got it connected to audio from the Sonos, to the DVR extender, to the XBox, etc. (It has a radio tuner as well). It’s a simple system and reasonably configurable if you’re handy with technology (changing labels on the remote keypads for example). We love having our Sonos hooked up to this device. (It’s also connected to our UPS as a power fluctuation with this plugged in and physically switched on causes a very loud audible POP on all of the speakers).
  • K) Sonos Connect. Ah. We love this little guy (in combination with the Russound mentioned above). Audio everywhere we want it. Our audio. Not old school radio. We’ve got the Sonos app installed on every iDevice and tablet around the house so we can control the music anywhere.
  • L) Axis 241q (discontinued apparently). Super stable. Takes up 4 analog video camera feeds and converts them into IP feeds. So, rather than buying really expensive outdoor security cameras (they die too frequently!), I can buy reasonably priced security cameras and replace them without heartache. I’ve got Blue Iris running on the Windows 8 box and it’s an AMAZING application.  I talked about an older version on my web site 4 years ago (images are gone though). I just upgraded to version 3 and the product is just getting better! It chugs through all of the security camera footage and automatically records (to one of the external USB drives) and sends alert e-mails as needed when a configurable amount of “movement” is detected at our house. It’s great getting an image of the UPS guy dropping an Amazon package off at our house. The Axis devices are expensive, but they’ve been well reviewed, and I’ve not had any problem with ours.
  • M) I wanted to better track our electrical usage, so I hooked up three of the CT clamps and one of the displays from CurrentCost.net, and then hooked that up to the Windows 8 micro server (via a touchy COM to USB cable), and it uploads the data in near real time. Our solar panels mess the data up though … I’ve not been happy with the setup yet and haven’t had the interest to fix it (or the electrician).
  • N) roku. We bought an extra power adapter so we can carry this from room to room as we didn’t want to buy multiples of these (as we’d only ever use one at a time as our Internet bandwidth can’t really handle anything better).

    While we have an AppleTV, we love our Roku more. (Partly as we just haven’t decided to spend money on iTunes if Amazon has the same products). It’s not perfect, but it’s not expensive and it plays Amazon content more consistently than our “Internet enabled” Samsung TV.

  • O) Nest. I’ve talked about Nest a few times. <giggle>
  • P) XBox. We’ve got 2 of these. One connected to the main family room TV (for game playing), and a second to a small TV in our kitchen (for Media Center functionality).
  • Q) A Puget Systems Echo. A very small efficient PC that we use as a Windows Media Center. It’s directly connected to a Samsung LCD TV via HDMI. As the PC is too small to have a decent dual TV tuner, I’ve got a SiliconDust HDHome Run Dual Digital TV tuner tucked away in the unfinished basement area. It’s a great combination. The Echo is also setup to serve the Media Center experience to a second XBox in our kitchen. We currently use the Media Center to record local HD over-the-air broadcasts. Dish Network (our current satellite TV provider) doesn’t offer all of our local TV stations via HD. I’ve got an Antennas Direct ClearStream4 on our roof – best antenna we’ve ever owned.  It’s connected to the HDHome Run tuner. The Media Center is set to go into Standby when not used, so it’s energy efficient (certainly better than the satellite/cable provider’s DVRs that are energy hogs). (And while Satellite reception can be affected by weather, this combination always seems to get a decent signal so we can get news in the case of severe weather).
  • R) Dish Network Hopper – our internet connection isn’t always reliable enough to maintain HD streamed video content. (SD is always fine). We experimented with “cut the cable”, but after doing the math of what we wanted to watch, and the way we watch TV (almost always recorded, rarely live), the math worked out to be within $5 of the cost of satellite TV service a month. So, given that we’d get a lot more options with satellite TV, we stayed with that. (We don’t have cable TV as an option at our house). The Hopper is “meh.” It’s got more usability issues than I’d like, but it’s generally OK.
  • S) We installed 24 solar panels in late 2011.  The solar panels use micro inverters from enphase energy (model M210-84-240). Each inverter sends its status via the power-line to a small device called the Envoy. We’ve not been real happy with the setup as it’s very touchy. Sending signals over the power-lines can be a sketchy and inconsistent affair. When originally installed, we struggled to find a reasonable location within the house that had a strong signal from the micro inverters. Eventually we found a location.

    We’ve had one micro inverter fail and was replaced.

    The Envoy also needs wired internet access so that it can update a public/private web page with the latest data from the solar panels. It’s actually quite slick. Thankfully, there is no subscription service. (For the geeks, there is reasonable local access to the data through the device as well if you wanted to get direct access to the data without going through enphase and their “Enlighten” portal) The Envoy is powered by apparently a CPU slightly more powerful than a toaster. The thing takes minutes to boot. It’s really only frustrating when you’re trying to find a final resting spot for the device.
    image

  • T) 24 solar panels – SunPower 230-watt
  • U) We’ve got several wireless security cameras scattered about internally. Using Blue Iris (mentioned above), they only record/watch during hours that we’re typically away. I talked more about the cameras here. They both were Asante most recently.
  • V) We’ve gone through so many wired security cameras over the years, I’ve lost track and am not really sure what we have actively installed anymore. Several of the cameras are color. I always buy color these days as the price difference isn’t substantial. What I have noticed is that you should always have a backup power supply ready. It’s the thing that typically goes on these camera setups first (and if you start seeing “wavy lines”, replace the power supply as it’s almost always been the culprit in my experience). Most of them all use the same type of power supply, so I now have a few backups. I buy all of the various supplies from Monoprice.

    We have two cameras that have night time IR capabilities. Ignore what they say on the packaging for “night vision” distances. They’re always wrong. Further, the “vision” is so weak at the far end of the distance to be absolutely useless. Practically speaking, I’d take the distance they suggest and at least cut it in half. Also, make sure you consider the distance from the mount to the ground in your calculations. While I like the idea of IR, it works so poorly on our cameras that I couldn’t recommend any specific reasonably priced model. If you’re expecting to be able identify someone using the IR, good luck. You might be able to tell the difference from a human or a sasquatch, on a good night.

And that represents the majority of things we’ve got setup in our house right now. I’ve tried to include links to the original product page or where I bought a given product if available (or to a newer model if the older one has been discontinued).

I’m so embarrassed by the state of the wiring and setup of the majority of the equipment mentioned above that I’m not going to include a photo. It looks like a child’s messy toy chest, filled with wires and various rectangular boxes with blinking lights.

Nest Thermostat API using Node JS and Nest API Update

I’ve been asked by a few people for more details on the API Nest Labs uses for their thermostats, especially regarding setting data (and not just polling).

The API uses mostly JSON formatted data POSTed to their web servers.

Authentication

To authenticate, POST the username and password, encoded as form url-encoded:

POST https://home.nest.com/user/login HTTP/1.1
Host: home.nest.com
Proxy-Connection: keep-alive
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Accept-Language: en-us
Content-Length: {Length}
Accept: */*
Connection: keep-alive
User-Agent: Nest/3.0.1.15 (iOS) os=6.0 platform=iPad3,1

username={email}&password={password}

Adjust the email and password, and the content length to fit. You may need to remove the Accept-Encoding header value if your client cannot accept gzip or deflated responses.

The server responds with a healthy set of basic information (in JSON format):

{
    "is_superuser": false,
    "is_staff": false,
    "urls": {
        "transport_url": "https://{subdomain}.transport.nest.com:443",
        "rubyapi_url": "https://home.nest.com/",
        "weather_url": "http://www.wunderground.com/auto/nestlabs/geo/current/i?query=",
        "support_url": "https://nest.secure.force.com/support/webapp?"
    },
    "limits": {
        "thermostats_per_structure": 10,
        "structures": 2,
        "thermostats": 10
    },
    "access_token": "GIANT TOKEN STRING==",
    "userid": "1234",
    "expires_in": "Wed, 07-Oct-2012 12:08:00 GMT",
    "email": "user@example.com",
    "user": "user.1234"
}

There are a few things you’ll need from the response:

  • transport_url : this is the address for all of the later request that are made. I’d speculate it’s just a specific server in a server farm (likely with server affinity/session)
  • access_token : this is the key for all later requests and grants access to the API
  • userid/user : a unique user ID
  • expires_in : this is the timestamp for when the access token expires

I’m not sure why the “limits” are being sent back to the client.

You can obtain the service URLs at any time:

POST https://home.nest.com/user/service_urls HTTP/1.1
Host: home.nest.com
Authorization: Basic GIANT TOKEN STRING==
Accept-Encoding: gzip, deflate
Accept: */*
Content-Length: 0
Accept-Language: en-us
Connection: keep-alive
Proxy-Connection: keep-alive
User-Agent: Nest/3.0.1.15 (iOS) os=6.0 platform=iPad3,1

Just insert an Authorization header with the access_token value.

The response:

{ "urls": { "transport_url": "https://{subdomain}.transport.nest.com:443", "rubyapi_url": "https://home.nest.com/", "weather_url": "http://www.wunderground.com/auto/nestlabs/geo/current/i?query=", "support_url": "https://nest.secure.force.com/support/webapp?" }, "limits": { "thermostats_per_structure": 10, "structures": 2, "thermostats": 10 } }

Nest labs has a special URL at wunderground.com to access the weather.

One of the first requests you might want to send is to get a complete picture of the system:

GET https://{subdomain}.transport.nest.com:443/v2/mobile/#USER.ID# HTTP/1.1
Host: {subdomain}.transport.nest.com:443
Authorization: Basic GIANT TOKEN STRING==
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: en-us
Connection: keep-alive
X-nl-protocol-version: 1
X-nl-user-id: #USERID#
Proxy-Connection: keep-alive
User-Agent: Nest/3.0.1.15 (iOS) os=6.0 platform=iPad3,1

You’ll make the request to the transport_url and make sure that the Host, Authorization, and X-nl-user-id header values are set appropriately. The Url now must include though:

  • version
  • mobile
  • and the full user id (like user.1234)

So, it will look something like: /v2/mobile/user.1234

It will respond with the mother-load of all JSON payloads. I’ve trimmed the response for my house as I have three thermostats. But the pattern repeats exactly, so it’s easy to extrapolate how the pattern works:

{ "metadata": { "SERIALNUM1": { "$version": -1262653277, "$timestamp": 1349697004000, "last_connection": 1349697004683, "last_ip": "LAST.IP.ADDRESS" }, "SERIALNUM2": { "$version": -1868790132, "$timestamp": 1349696678000, "last_connection": 1349696678701, "last_ip": "LAST.IP.ADDRESS" }, "SERIALNUM3": { "$version": -1581663504, "$timestamp": 1349696680000, "last_connection": 1349696680647, "last_ip": "LAST.IP.ADDRESS" } }, "track": { "SERIALNUM2": { "$version": 1065037529, "$timestamp": 1349696725390, "online": true, "last_connection": 1349696725390, "last_ip": "LAST.IP.ADDRESS" }, "SERIALNUM3": { "$version": 981680556, "$timestamp": 1349696726266, "online": true, "last_connection": 1349696726266, "last_ip": "LAST.IP.ADDRESS" }, "SERIALNUM1": { "$version": 1421919505, "$timestamp": 1349697004728, "online": true, "last_connection": 1349697004728, "last_ip": "LAST.IP.ADDRESS" } }, "user_settings": { "#USERID#": { "$version": 370836640, "$timestamp": 1337481029003, "email_verified": true, "tos_accepted_version": 1319500800000, "receive_marketing_emails": true, "receive_nest_emails": true, "receive_support_emails": true, "max_structures": 2, "max_thermostats": 10, "max_thermostats_per_structure": 10, "tos_minimum_version": 1319500800000, "tos_current_version": 1319500800000, "lang": "en_US" } }, "structure": { "#STRUCTURE-UUID#": { "$version": 1797929878, "$timestamp": 1349689810000, "location": "Mount Horeb, WI", "renovation_date": "2000", "country_code": "US", "away_timestamp": 1349302501, "away": false, "house_type": "family", "name": "Home", "postal_code": "#POSTALCODE#", "creation_time": 1324159145719, "num_thermostats": "3", "devices": ["device.SERIALNUM3", "device.SERIALNUM1", "device.SERIALNUM2"], "user": "user.#USERID#", "away_setter": 1 } }, "link": { "SERIALNUM3": { "$version": 2122853931, "$timestamp": 1327246591000, "structure": "structure.#STRUCTURE-UUID#" }, "SERIALNUM2": { "$version": -1703839727, "$timestamp": 1324159215000, "structure": "structure.#STRUCTURE-UUID#" }, "SERIALNUM1": { "$version": -459415854, "$timestamp": 1325967612000, "structure": "structure.#STRUCTURE-UUID#" } }, "device": { "SERIALNUM1": { "$version": -81037153, "$timestamp": 1349696605000, "heatpump_setback_active": false, "emer_heat_enable": false, "local_ip": "LOCAL.IP.ADDRESS", "switch_system_off": false, "away_temperature_high": 27.778, "temperature_lock_high_temp": 22.222, "cooling_source": "electric", "leaf_threshold_cool": 0.0, "fan_cooling_state": false, "note_codes": [], "heater_source": "gas", "compressor_lockout_leaf": -17.8, "has_x3_heat": false, "target_humidity_enabled": false, "heat_x3_source": "gas", "alt_heat_delivery": "forced-air", "fan_mode": "auto", "has_x2_heat": false, "rssi": 67.0, "emer_heat_delivery": "forced-air", "heatpump_savings": "off", "pin_y2_description": "none", "filter_reminder_enabled": false, "capability_level": 3.0, "schedule_learning_reset": false, "has_x2_cool": false, "hvac_pins": "W1,Y1,C,Rc,G", "ob_orientation": "O", "range_enable": true, "auto_away_enable": true, "dual_fuel_breakpoint_override": "none", "lower_safety_temp_enabled": true, "has_fan": true, "dehumidifier_state": false, "range_mode": false, "nlclient_state": "", "emer_heat_source": "electric", "heatpump_ready": false, "available_locales": "en_US,fr_CA,es_US", "current_version": "3.0.1", "learning_state": "slow", "pin_ob_description": "none", "pin_rh_description": "none", "has_alt_heat": false, "pin_y1_description": "cool", "humidifier_state": false, "backplate_serial_number": "#BACKPLATE-SERIALNUMBER1#", "has_x2_alt_heat": false, "heat_x3_delivery": "forced-air", "leaf_threshold_heat": 19.336, "has_emer_heat": false, "learning_mode": true, "leaf_learning": "ready", "has_aux_heat": false, "aux_heat_source": "electric", "backplate_bsl_info": "BSL", "alt_heat_x2_source": "gas", "pin_c_description": "power", "humidifier_type": "unknown", "pin_w2aux_description": "none", "country_code": "US", "target_humidity": 35.0, "heat_x2_delivery": "forced-air", "lower_safety_temp": 4.444, "cooling_x2_source": "electric", "equipment_type": "gas", "heat_pump_aux_threshold": 10.0, "alt_heat_x2_delivery": "forced-air", "heat_pump_comp_threshold": -31.5, "learning_days_completed_cool": 116, "backplate_bsl_version": "1.1", "current_schedule_mode": "HEAT", "hvac_wires": "Heat,Cool,Fan,Common Wire,Rc", "leaf": false, "type": "TBD", "pin_g_description": "fan", "switch_preconditioning_control": false, "click_sound": "on", "aux_heat_delivery": "forced-air", "away_temperature_low_enabled": true, "heat_pump_comp_threshold_enabled": false, "preconditioning_ready": true, "has_dehumidifier": false, "fan_cooling_enabled": true, "leaf_away_high": 28.88, "fan_cooling_readiness": "ready", "device_locale": "en_US", "temperature_scale": "F", "error_code": "", "preconditioning_active": false, "battery_level": 3.93, "away_temperature_high_enabled": true, "learning_days_completed_heat": 149, "pin_star_description": "none", "upper_safety_temp_enabled": false, "preconditioning_enabled": true, "current_humidity": 45, "dual_fuel_breakpoint": -1.0, "postal_code": "#POSTALCODE#", "backplate_mono_version": "4.0.5", "alt_heat_source": "gas", "aux_lockout_leaf": 10.0, "has_heat_pump": false, "heater_delivery": "forced-air", "auto_away_reset": false, "away_temperature_low": 14.444, "radiant_control_enabled": false, "temperature_lock": false, "upper_safety_temp": 35.0, "time_to_target_training": "ready", "dehumidifier_type": "unknown", "target_time_confidence": 1.0, "temperature_lock_low_temp": 20.0, "pin_w1_description": "heat", "forced_air": true, "temperature_lock_pin_hash": "", "leaf_type": 1, "backplate_mono_info": "TFE (BP_DVT) 4.0.5 (root@bamboo) 2012-09-18 18:18:23", "has_dual_fuel": false, "learning_time": 2113, "creation_time": 1325966794212, "has_humidifier": false, "learning_days_completed_range": 0, "leaf_schedule_delta": 1.11, "user_brightness": "auto", "leaf_away_low": 13.92, "pin_rc_description": "power", "serial_number": "SERIALNUM1", "mac_address": "18b43004f391", "heat_x2_source": "gas", "time_to_target": 0, "backplate_model": "Backplate-1.9", "model_version": "Diamond-1.10", "heat_pump_aux_threshold_enabled": true }, "SERIALNUM3": { "$version": 2134103145, "$timestamp": 1349695665000, /* same as previous */ "backplate_model": "Backplate-1.9", "model_version": "Diamond-1.10", "heat_pump_aux_threshold_enabled": true }, "SERIALNUM2": { "$version": -1340728480, "$timestamp": 1349692217000, /* same as previous */ "backplate_model": "Backplate-1.9", "model_version": "Diamond-1.10", "heat_pump_aux_threshold_enabled": true } }, "schedule": { "SERIALNUM3": { "$version": -1130522241, "$timestamp": 1349692663000, "days": { "3": { "3": { "time": 74700, "entry_type": "setpoint", "temp": 20.0, "type": "HEAT" }, "2": { "time": 23400, "entry_type": "setpoint", "temp": 14.444, "type": "HEAT" }, "1": { "time": 19800, "entry_type": "setpoint", "temp": 17.222, "type": "HEAT" }, "0": { "touched_by": 1, "time": 0, "touched_tzo": -18000, "entry_type": "continuation", "temp": 14.444, "type": "HEAT", "touched_at": 1349285499 }, "4": { "time": 78300, "entry_type": "setpoint", "temp": 14.444, "type": "HEAT" } }, "2": { "3": { "time": 74700, "entry_type": "setpoint", "temp": 20.0, "type": "HEAT" }, "2": { "time": 23400, "entry_type": "setpoint", "temp": 14.444, "type": "HEAT" }, "1": { "time": 19800, "entry_type": "setpoint", "temp": 17.222, "type": "HEAT" }, "0": { "touched_by": 1, "time": 0, "touched_tzo": -18000, "entry_type": "continuation", "temp": 14.444, "type": "HEAT", "touched_at": 1349285499 }, "4": { "time": 78300, "entry_type": "setpoint", "temp": 14.444, "type": "HEAT" } }, "1": { "3": { "time": 74700, "entry_type": "setpoint", "temp": 20.0, "type": "HEAT" }, "2": { "time": 23400, "entry_type": "setpoint", "temp": 14.444, "type": "HEAT" }, "1": { "time": 19800, "entry_type": "setpoint", "temp": 17.222, "type": "HEAT" }, "0": { "touched_by": 1, "time": 0, "touched_tzo": -18000, "entry_type": "continuation", "temp": 14.444, "type": "HEAT", "touched_at": 1349285499 }, "4": { "time": 78300, "entry_type": "setpoint", "temp": 14.444, "type": "HEAT" } }, "0": { "3": { "time": 74700, "entry_type": "setpoint", "temp": 20.0, "type": "HEAT" }, "2": { "time": 23400, "entry_type": "setpoint", "temp": 14.444, "type": "HEAT" }, "1": { "time": 19800, "entry_type": "setpoint", "temp": 17.222, "type": "HEAT" }, "0": { "touched_by": 1, "time": 0, "touched_tzo": -18000, "entry_type": "continuation", "temp": 14.444, "type": "HEAT", "touched_at": 1349285499 }, "4": { "time": 78300, "entry_type": "setpoint", "temp": 14.444, "type": "HEAT" } }, "6": { "3": { "time": 67500, "entry_type": "setpoint", "temp": 18.333, "type": "HEAT" }, "2": { "time": 28800, "entry_type": "setpoint", "temp": 14.444, "type": "HEAT" }, "1": { "time": 24300, "entry_type": "setpoint", "temp": 17.222, "type": "HEAT" }, "0": { "touched_by": 1, "time": 0, "touched_tzo": -18000, "entry_type": "continuation", "temp": 14.444, "type": "HEAT", "touched_at": 1349285499 }, "4": { "time": 75600, "entry_type": "setpoint", "temp": 14.444, "type": "HEAT" } }, "5": { "3": { "time": 67500, "entry_type": "setpoint", "temp": 18.333, "type": "HEAT" }, "2": { "time": 28800, "entry_type": "setpoint", "temp": 14.444, "type": "HEAT" }, "1": { "time": 24300, "entry_type": "setpoint", "temp": 17.222, "type": "HEAT" }, "0": { "touched_by": 1, "time": 0, "touched_tzo": -18000, "entry_type": "continuation", "temp": 14.444, "type": "HEAT", "touched_at": 1349285499 }, "4": { "time": 75600, "entry_type": "setpoint", "temp": 14.444, "type": "HEAT" } }, "4": { "3": { "time": 74700, "entry_type": "setpoint", "temp": 20.0, "type": "HEAT" }, "2": { "time": 23400, "entry_type": "setpoint", "temp": 14.444, "type": "HEAT" }, "1": { "time": 19800, "entry_type": "setpoint", "temp": 17.222, "type": "HEAT" }, "0": { "touched_by": 1, "time": 0, "touched_tzo": -18000, "entry_type": "continuation", "temp": 14.444, "type": "HEAT", "touched_at": 1349285499 }, "4": { "time": 78300, "entry_type": "setpoint", "temp": 14.444, "type": "HEAT" } } }, "schedule_mode": "HEAT", "name": "Basement Current Schedule", "ver": 2 }, "SERIALNUM2": { "$version": -462155699, "$timestamp": 1349674697000, "days": { /* SAME AS ABOVE */ } }, "schedule_mode": "HEAT", "name": "Second Floor Current Schedule", "ver": 2 }, "SERIALNUM1": { "$version": 2014520777, "$timestamp": 1349695806000, "days": { /* SAME AS ABOVE */ } }, "schedule_mode": "HEAT", "name": "First Floor Current Schedule", "ver": 2 } }, "shared": { "SERIALNUM3": { "$version": -493517056, "$timestamp": 1349696367000, "auto_away": 0, "auto_away_learning": "training", "hvac_heat_x3_state": false, "hvac_alt_heat_state": false, "compressor_lockout_enabled": false, "target_temperature_type": "heat", "hvac_heater_state": false, "hvac_emer_heat_state": false, "can_heat": true, "compressor_lockout_timeout": 0, "hvac_cool_x2_state": false, "target_temperature_high": 24.0, "hvac_aux_heater_state": false, "hvac_heat_x2_state": false, "target_temperature_low": 20.0, "target_temperature": 14.444, "hvac_ac_state": false, "hvac_fan_state": false, "target_change_pending": false, "name": "Basement", "current_temperature": 18.11, "hvac_alt_heat_x2_state": false, "can_cool": true }, "SERIALNUM1": { "$version": -1432433268, "$timestamp": 1349696363000 /* SAME AS ABOVE */ }, "SERIALNUM2": { "$version": 2060664119, "$timestamp": 1349696709000 /* SAME AS ABOVE */ } }, "user_alert_dialog": { "#USERID#": { "$version": -1852987123, "$timestamp": 1327246591000, "dialog_data": "", "dialog_id": "confirm-pairing" } }, "user": { "#USERID#": { "$version": 209478897, "$timestamp": 1324159145000, "name": "EMAILADDRESS", "structures": ["structure.#STRUCTURE-UUID#"] } } }

Setting a Temperature

Changing a thermostat’s current set point is easy.

You’ll need the Serial Number (shown as SERIALNUM1 in the JSON above) of the thermostat.

POST https://275be85a.transport.nest.com:443/v2/put/shared.01AA01AB4611009P HTTP/1.1
Host: 275be85a.transport.nest.com:443
Accept-Language: en-us
User-Agent: Nest/3.0.1.15 (iOS) os=6.0 platform=iPad3,1
X-nl-base-version: 2060664119
Accept: */*
Content-Type: application/json
X-nl-protocol-version: 1
X-nl-user-id: 7236
X-nl-session-id: ios-7236-371385438.528577
Connection: keep-alive
X-nl-merge-payload: true
Authorization: Basic GIANT TOKEN STRING==
Content-Length: 60
Proxy-Connection: keep-alive
Accept-Encoding: gzip, deflate

{"target_change_pending":true,"target_temperature":16.11111}

Set the temperature in Celsius.

There’s also a polling subscription that happens. It’s extremely chatty and from the amount of polling it does, you’d think that the UI was doing live graphing of micro-temperature changes.

Essentially, the polling sends a series of keys, with timestamps, representing the various types of data being requested.

It looks something like this:

{ "keys": [{ "key": "user.#USERID#", "version": 209478897, "timestamp": 1324159145000 }, { "key": "user_settings.#USERID#", "version": 370836640, "timestamp": 1337481029003 }, { "key": "user_alert_dialog.#USERID#", "version": -1852987123, "timestamp": 1327246591000 }, { 

It repeats for sections such as “shared”, “message”, “device”, “track”, and more. For my purposes, shared is the winner as it contains the current temperature.

 

Node API

I decided to write a new demonstration application and polish it up a bit, this time using Node.

I’m not going to document the API that I created here (not right now), but here’s a sample of how it can be used:

var username = process.argv[2];
var password = process.argv[3];

if (username && password) {
    username = trimQuotes(username);
    password = trimQuotes(password);
    nest.login(username, password, function (data) {
        if (!data) {
            console.log('Login failed.');
            process.exit(1);
            return;
        }
        console.log('Logged in.');
        nest.fetchStatus(function (data) {
            for (var deviceId in data.device) {
                if (data.device.hasOwnProperty(deviceId)) {
                    var device = data.shared[deviceId];

                    console.log(util.format("%s [%s], Current temperature = %d F target=%d",
                        device.name, deviceId,
                        nest.ctof(device.current_temperature),
                        nest.ctof(device.target_temperature)));
                }
            }
            subscribe();
        });
    });
}

function subscribe() {
    nest.subscribe(subscribeDone);
}

function subscribeDone(deviceId, data) {
    if (deviceId) {
        console.log('Device: ' + deviceId)
        console.log(JSON.stringify(data));
    }
    setTimeout(subscribe, 2000);
}

The example code runs forever. Smile

I’ve included two methods in the API, “get” and “post” which make it simple to call additional web services that I haven’t yet provided.

Find the code here: https://github.com/wiredprairie/unofficial_nodejs_nest.

Update: there’s a npm as well now (Dec 20, 2012)

Nest Update #12: Software at 3.0 with New Features

As the blogosphere exploded yesterday with news of a second generation Nest thermostat and a new major version of the software (for the thermostats and the controllers such as the web site and various SmartPhones), I wondered what impact the new software and hardware would have on average users, like us.

Do check out the blog post though for full details as there are number of new features in the new device that aren’t available to first generation owners (especially as it relates to supporting a variety of HVAC systems).

image

Before the Nest thermostat was announced and all of the news about lack of support for various HVAC systems, I hadn’t heard of a second-stage cooling, third staging heating, etc. I’d never had them and didn’t know they existed! Smile Now, Nest claims to support up to 95% of HVACs installed in the USA (& Canada?).

I updated my iPhone to 3.0.1 of the Nest App this morning and checked out the new features. Two of my three thermostats had updated to firmware 3.0.1.

IMG_0630

I looked through one of the thermostat’s menus and while there are a few changes (new features), nothing major has changed in the interface. The overall usability is still quite good although I wonder about discoverability of features as the number of features grows.

The home screen hasn’t changed much at all. Still, the giant house:

IMG_0624

The user interface still requires rotation to horizontal to perform anything but the basic changes (such as temperature).

IMG_0625

I don’t know why the Home icon needs to be so prominent as it takes up valuable screen space on a tiny device (and has only a few useful features).

One of the new features is that for a given thermostat, you can actually toggle the fan to ON now if you want. Occasionally we missed that feature (from our old thermostats) when I’ve cooked something that causes an odor to, politely, linger, for a while longer than we’d like. Smile

The feature is buried though under the SETTINGS for a thermostat, and then select AT A GLANCE.

IMG_0626

Toggle it to turn the fan on temporarily.

The remaining values are:

  • Outside temperature (50F)
  • Current inside temperature (68F)
  • Current Humidity (46%)
  • Current set point temperature (69F)

Under the menu, NEST SENSE, you’ll find a one big new thing and a few layout/naming changes:

IMG_0627

EARLY-ON!

IMG_0628

Early-on is a feature that many early adopters had wanted and expected from a modern thermostat. We slapped our heads, cried, complained, hugged…, dismayed that it wasn’t there. And, glory to Nest Labs, they finally added it.

Now, your house can actually be warm/cool when you want. For example, it can be warm when you get out of bed, not just start warming when you get out of bed. This is a HUGE add and I’m very glad Nest has finally added it!

The schedule remains unchanged:

IMG_0629

The overlapping circles still look a bit cluttered to my eye, but it gets the job done.

Under the Home Settings, you’ll find what amount to some survey questions:

 

IMG_0633

IMG_0635

IMG_0637

I’m very pleased with the upgrade to the firmware and smart phone/tablet software. It adds some absolutely needed features.

As an update to my experience with Nest, I’ve definitely had fewer problems lately than I had during the first 6 to 8 months. While I still don’t applaud Nest for their activity in social media and reaching out to their customers proactively (as they never responded to any of the comments here), they have been active behind the scenes.

The wireless connectivity to the thermostats has improved. I haven’t noticed the same problems as before and I THANKFULLY haven’t had to re-add my account or Wifi information in months to any of our thermostats.

I’ve had enough good success recently to change my recommendation on Nest Thermostats. If you’re in the market for a new thermostat, and you’ve got $249US to spend, I’d say it should definitely be a strong contender.

Read through the comments and the issues – but understand a lot of the issues have been resolved.

You can buy the older model while supplies last for $229.

The second generation is available for preorder from Amazon today.

I think it’s ready to be part of your house (after you check your system’s compatibility).

If you have found these posts useful, please consider using the Amazon links above to buy your shiny new Nest thermostat (especially if you have Prime!) as a way of saying thanks!

What do you think of the updates and new hardware?

How to find an element in a DataTemplate in WinRT/XAML.

Here’s one way to find a named element in a DataTemplate in XAML in Windows 8 XAML.

You might try FindName to discover it doesn’t work. That’s because it’s not recursive.

So, I created a simple extension method to do the same thing:

public static class FrameworkElementExtensions
{
    public static FrameworkElement FindDescendantByName(this FrameworkElement element, string name)
    {
        if (element == null || string.IsNullOrWhiteSpace(name)) { return null; }

        if (name.Equals(element.Name, StringComparison.OrdinalIgnoreCase))
        {
            return element;
        }
        var childCount = VisualTreeHelper.GetChildrenCount(element);
        for (int i = 0; i < childCount; i++)
        {
            var result = (VisualTreeHelper.GetChild(element, i) as FrameworkElement).FindDescendantByName(name);
            if (result != null) { return result; }
        }
        return null;
    }
}

The code above loops through the children and attempts to match by name.

A simple usage (admittedly, this is stupid and clunky as lists are often virtualized, but …)

private void Page_Loaded_1(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
    for (int i = 0; i < myPeeps.Items.Count; i++)
    {
        var element = myPeeps.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement;
        if (element != null)
        {
            var tb = element.FindDescendantByName("tbValue") as TextBlock;
            if (tb != null)
            {
                tb.Text = i.ToString();
            }
        }
    }
}

Given this DataTemplate and Page:

<Page.Resources>
    <local:SampleDataSource x:Key="sampleData" />
    <DataTemplate x:Key="SampleDataTemplate">
        <Grid >
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <TextBlock HorizontalAlignment="Left" Text="{Binding Name}" />
            <TextBlock Grid.Row="1" Name="tbValue" />
        </Grid>
    </DataTemplate>
</Page.Resources>

<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}" 
      DataContext="{StaticResource sampleData}">
    <ItemsControl x:Name="myPeeps"
        ItemsSource = "{Binding Persons}"
        ItemTemplate="{StaticResource SampleDataTemplate}" FontSize="22"/>
</Grid>

And this sample data:

public class Person {
    public string Name { get; set; }        
}    

public class SampleDataSource {
    public IEnumerable<Person> Persons { get; private set; }
    
    
    public SampleDataSource () {
        var list = new List<Person>();
        
        list.Add(new Person { Name = "Alex" });
        list.Add(new Person { Name = "Betsy" });
        list.Add(new Person { Name = "Carla" });
        list.Add(new Person { Name = "Donald" });
        list.Add(new Person { Name = "Erica" });
        list.Add(new Person { Name = "Francis" });            
        
        this.Persons = list;
    }            
}

You’ll get results like this:

image

Stay away from Gander Mountain Academy

My wife and I were interested in signing up for some training and use of the Virtual Range at the Gander Mountain Academy. It’s a very interesting facility available in a few places across the United States.

As I was signing up, I needed to agree to the Rules and Fees. I expected to read about the dangers of the facility (as there is a live-fire range there as well), but I didn’t expect to read that we would need to give up all rights to our likeness for any purposes forever. It’s unfortunately a typical clause in an sweepstakes where you’re being given something (for nothing). But, as the Gander Mountain Academy classes and ranges and simulators are anything but free, I certainly didn’t expect this:

(I can’t find a way to directly link to the agreement without putting a course in a cart. To see it for your self: https://gandermtnacademy.gandermountain.com/purchase-now/wichita-ks, pick training, then one of the courses, a Date/time, and then look for the Rules and Fees link near the bottom of the confirmation page)

2. Each Participant hereby grants Gander Mountain Company and its designees, and their respective affiliates, licensees, successors and assigns, (a) the right to capture, record or memorialize by use of any technology whatsoever, including, without limitation, by photography, video recording or audio recording, any Participant during any participation in any Gander Mtn. Academy activities; and (b) an unrestricted, absolute, universal, perpetual, irrevocable, non-royalty bearing, and transferable right and license (but not any obligation) to use, copy, reproduce, transmit, distribute, display, modify, perform, present, publish, transform, create works and derivative works, and otherwise promote or utilize each of their image, likeness, voice, words and/or other personal attributes captured, recorded or memorialized in any manner by Gander Mtn. Company or its designees, in any form, format, medium or media whether now or hereafter existing (including, without limitation, print, direct mail, catalog, in-store display, online, mobile or wireless communications, radio or television broadcast, telecast or photograph), in whole or in part, individually or in conjunction with other photographs, recordings, images or materials, whether in a realistic, artistic or composite rendering, for any purpose whatsoever (including, without limitation, in connection with the creation, advertising, sale and/or promotion of any products and/or services of Gander Mountain Company and/or its designees (including, without limitation, the Gander Mtn. Academy), and without any consideration, notice, consent or attribution by or to any of them or any third party. Each Participant hereby forever and irrevocably waives any rights to any of the foregoing and understands and agrees that Gander Mountain Company and its designees are the exclusive owners of any and all right, title and interest, including copyright, in and to any such materials.

I’m sure they have security cameras, and I accept that they are recording the people who come and go through their facility, but this is far far over the top.

Essentially, they can later use your likeness, including your voice, and words in any way they see fit.

We’re not using their facility or shopping at any of their stores until this changes. I encourage you to do the same. And tell Gander Mountain why!

If I hear back from them, I’ll update this post. What do you think of their terms?