WiredPrairie

A little bit of everything: software, apps, usability, programming, design and whatever else

  • Home
  • About
  • Contact Me
  • Cooking
  • Nest Thermostat
  • Old Archives
  • Photography
  • Quick Searches
  • You need this

EzData for Ember.js

Posted by Aaron on Sunday, January 1st 2012    Tweet

Previous post: Entity reference

I’ve been extending my original post (above) in an effort to create a simple entity system using Ember.js (SproutCore 2.0). While it’s grown in sophistication, it is far from complete. Smile

The project is now located on GitHub.

This is the introduction, from the readme on GitHub.

EmberJS – EZData

EZData is a library intended to a simple way of accessing data from a relational database when using Ember.js. It will never try to be everything to everyone. :) Instead, it’s intended to be small, efficient, and easy to learn and use javascript library.

The basic functionality is that it’s designed to manipulate individual entities (or Records), not a complex object structure that is often stored as JavaScript objects. This mirrors what’s found in many web systems today in the data model.

Right now, the project is brand new and quite in flux.

Essentials

Create types by calling Entity.define (instead of the typical Ember.Object.extend).

DemoApp.Person = Entity.define(Entity, "Person", {
    firstName:String,
    lastName:String,
    DOB:Date,

    fullName:function () {
        return this.get('lastName') + ", " + this.get('firstName');
    }.property('firstName', 'lastName')
});

This will create a new Class internally by using Ember.extend but it also does some other magic (e.g., creating a data store for all entities of a given type). First and foremost, by calling define, it’s expected that you’re mirroring a relational data structure of some sort which may have foreign key relationships to other tables.

In the example above, the Person entity is expected to have the following columns in a database table:

  • id => Number (from the basic Entity type)
  • firstName => String
  • lastName => String
  • DOB => DOB

Each property is assigned the basic data type that it will contain. The data type is used for linking and serialization.

Linking

The second example is a Gift:

DemoApp.Gift = Entity.define(Entity, "Gift", {
    from:DemoApp.Person
});

The Gift class is more interesting as one of the properties links to another type/Class (Person). As a direct connection to a Person as an object instance isn’t possible in traditional relational tables, some automatic linking occurs by declaring this linkage.

The from property is mapped to a second, hidden property that is only used for the foreign key relationship. By default, the hidden property will be calledfromPersonId. This can be overridden by creating a custom naming function, replacing the default stored atEntity.Settings.FOREIGN_KEY_GENERATOR_FUNCTION.

By making the linkage between the two types in the way demonstrated above, the standard Ember.js handlebar templating engine (and automatic value updating) features just work (see from.fullName below).

{{#each gifts}}
<tr>
    <td>
        {{ name }}
    </td>
    <td>
        {{ excitement }}
    </td>
    <td>
        {{ from.fullName }}
    </td>
</tr>

The fullName property of Person is a computed property, and continues to work as expected.

Getting the Data

If you want to retrieve a list from one of the data stores (or data tables), it’s a simple as calling the live function for one of the stores.

In the example below, there are two live collections. The first returns all of the gifts, and the second only returns gifts that have a lowercase letter o in the name.

// create some "live" connections to the data store
DemoApp.mainController.set('gifts', Entity.Stores.get(DemoApp.Gift).live());
// create a live connection with a filter (returns true if it contains the letter 'o')
DemoApp.mainController.set('giftsFiltered', Entity.Stores.get(DemoApp.Gift).live(
    function () {
        return this.get('name').indexOf('o') > -1;
    }));

To get access to one of the automatically created data stores, call Entity.Stores.get({Class}) where {Class} is the type that was created using thedefine method.

Notes / Cautions

Right now, all entities are required to have a property called id and be of type Number.

Filed under: Coding     Tags: binding, Ember.JS, JavaScript, SproutCore, templates
1 Comment   

Nest Thermostat Review, Update #3

Posted by Aaron on Saturday, December 31st 2011    Tweet

Sorry, if you’re tiring of reading these as I write about the experience of buying a Nest thermostat. Just move along if you’re not interested. :-)

Here’s the support e-mail I just sent Nest (via their online contact form) (Dec. 31, 2011, 9:15am):

We have three Nest thermostats in our house on a zoned system.

 

One of the thermostats has begun to often register a temperature (i.e, the current room temperature) that is more than several degrees (3-4) WARMER than the actual room temperature.

 

I have a digital non-contact thermometer that reads the surface temperature of whatever it is pointed at (not this model, but very similar in function: http://amzn.to/tbp0RH). Right now, for example, the wall temperature around the thermostat is about 66F. The temperature of the device reads 71F. Last night, it said 73F when the wall next to the thermostat was 64F. I have a second portable thermometer that confirms the air temperature very near the nest thermostat is around 66F.

We have a standard forced air heating system (no radiant).

 

The surface temperature of the nest reads 72F right now.

This wasn’t happening that we noticed when we first installed the unit and only started happening in the last week. I have not seen this problem with the other Nest thermostats in our home.

 

I’ll post updates here about their response to the issue. (The web site suggests it will be an hour or two before someone responds).

Has anyone else confirmed the Nest reported versus actual room temperature is accurate on their installed thermostats?

Update December 31, 2011

About six hours from my original request for support, I received a phone call from a Nest support engineer. (Note to Nest: you said the wait would be 1 to 2 hours …, manage expectations!)

We talked through the problem and he had me swap the thermostat from one floor to another. It’s relatively easy to do – but I did need to adjust the programming for both thermostats then. He promised to call me back Monday afternoon to see if things have improved (or changed at least). Of course, the issue should show up now in the basement (which is the thermostat I swapped the problem thermostat with).

Update #6, Update #5, Update #4, Update #3, Update #2, Update #1, Install

Filed under: General     Tags: Experience, Issue, Nest, Problem, Review
19 Comments   

Nest Thermostat Review, Update #2

Posted by Aaron on Friday, December 30th 2011    Tweet

I’ve discussed my Nest thermostat experience a few times and am slowly becoming less convinced that it is ready for the market if you’re at all technically savvy and you’re easily frustrated by things not working the way you’d expect (like, you know how to setup e-mail on your phone).

Update #6, Update #5, Update #4, Update #3, Update #2, Update #1, Install

Here’s a image of the schedule for the second floor, where my computer/den is located.

image

image

On Monday and Tuesday of this week, my wife and I had the day off and were home most of the day. We spent a lot of time upstairs as her computer and crafting area is on the second floor, as is my den. So, the heat was turned up most of the day.

The “learning” mode of the thermostat decided that as we were home two days in a row, that the whole week likely was going to look like that apparently. As you can see, the same schedule was replicated through all week days. image

On a normal morning, I often go to my den and do a bit of tinkering before leaving for work. However, I rarely turn up the thermostat and instead just leave it at the preset temperature. I’m not in my den long enough to justify the amount of energy it would take to heat the second floor.

So, it’s frustrating that the thermostat would turn up the heat automatically at 9:30am, long after I’ve left for work and then run it all day long. Thankfully, after 3 hours, it apparently realizes there’s no one home, and will automatically adjust the temperature.

Since I didn’t turn the heat UP, I don’t expect to need to turn it DOWN before I leave (I leave earlier than 9:30am). (Turn it down from what?)

I could turn off the learning features. But, then one of the key features of the thermostat is turned off:

image

GregN left a comment yesterday where he mentioned that Nest support recommended to try turning of the learning feature (activating “learning pause”).

Now, I need to go fix the schedule to reflect my reality. Again.

Nest, are you listening? This is a perfect example of my user experience not matching with the expectations Nest has set.

Update (December 30, 2011)

I tweeted this post (and directed it at nest and they did respond):

image

Update (December 31, 2011)

My wife had the day off on Friday (Dec 30) and adjusted the downstairs thermostat after lunch. Apparently, the thermostat believes that’s going to be a new routine for Friday’s (at about 1:30pm, temperature is set to 68F).

Just the day before I’d fixed the schedule for every day. It’s not taking into account manual schedule changes and giving them proper weighting.

image

The learning algorithm needs some help.

Filed under: General, Recommendations, Usability     Tags: Experience, Issue, Nest, Problem, Review
11 Comments   

Creating a simple Entity Reference system for Ember.js

Posted by Aaron on Wednesday, December 28th 2011    Tweet

I had some data stored in a structure similar to this:

image

Nothing too fancy. A table of Gift(s) and a table of Person(s). Each gift had a foreign key relationship to a Person (the person who “gave” the gift).

Since some people may give several gifts, I didn’t want to load the same data multiple times (each gift shouldn’t have the person’s name for example spelled out). I wanted to build a RESTful web service to return the gifts and persons as independent queries, with the resulting JavaScript objects being very similar to the original data storage in the database.

Using Ember.JS I built a small demonstration of one way that this could be done. I am aware of the in-progress data library for ember.js on github, but it was way more than I wanted (or needed). Furthermore, I found it more far complex than I wanted (and I’m not particularly fond of the syntax it requires). So, I created a simple system that I’ll likely expand upon over time and post here.

Here’s the very basic demo using handlebars templates (a core feature of Ember.js).

<!DOCTYPE html>

<html>
<head>
    <title>Demo2-EmberJS</title>
</head>
<body>

    <script type="text/x-handlebars" data-template-name="gifts">
      <h1>{{ giftReason }}</h1>
      <table>
          <thead>
          <tr>
              <td>Description</td>
              <td>Thrill</td>
              <td>From</td>
          </tr>
          </thead>
          {{#each gifts}}
          <tr>
              <td>
                  {{ name }} 
              </td>
              <td>
                  {{ excitement }}
              </td>
              <td>
                  {{ person.fullName }}
              </td>
          </tr>
          {{/each}}
      </table>
    </script>

    <script src="demo2/jquery-1.7.1.js" type="text/javascript"></script>
    <script src="demo2/ember-0.9.3.js" type="text/javascript"></script>
    <script src="demo2/app.js" type="text/javascript"></script>
</body>
</html>

And here is the content of app.js file referenced above (the other files can be downloaded from the web):

var Demo2App;
Demo2App = Ember.Application.create();
window.Demo2App = Demo2App;

// standard class that has an 'id' property
var Entity = Ember.Object.extend({
    id:null
});

(function () {
    Ember.entityRef = function (property, type) {
        // auto build connection if property is in the form
        // of 'typenameID' if type is not specified
        if (arguments.length === 1 && property) {
            var l = property.length;
            if (l > 2 && property.substr(l - 2).toLowerCase() === 'id') {
                type = property.substr(0, l - 2);
            } else {
                throw new Error("Type not specified, and cannot automatically determine store name. Referenced property name must end with Id.");
            }
        }
        var fn = new Function("return Demo2App.Stores.find('" + type + "', this.get('" + property + "')); ");
        return Ember.computed(fn).property(property);
    };
})();

/*
 Class definitions
 */

Demo2App.Person = Entity.extend({
    firstName:'',
    lastName:'',

    fullName:function () {
        return this.get('lastName') + ", " + this.get('firstName');
    }.property('firstName', 'lastName')
});


Demo2App.Gift = Ember.Object.extend({
    personId:null,
    person:Ember.entityRef('personId')
});


// build in a data store API
(function () {
    // within a closure, we'll store the current stores

    var stores = {};

    var Store = Ember.Object.extend({
        name:null,
        _data:null,
        add:function (obj) {
            if (!obj) {
                return;
            }
            if (!obj.id) {
                return;
            }
            this.get('_data')[obj.id] = obj;
        },
        remove:function (obj) {
            if (!obj) {
                return;
            }
            if (!obj.id) {
                return;
            }
            var data = this.get('_data');
            delete data[obj.id];
        },

        find:function (id) {
            if (!id) {
                return;
            }
            return this.get('_data')[id];
        }
    });

    Demo2App.Stores = Demo2App.Stores || {};

    Demo2App.Stores.create = function (name, options) {
        name = name.toLowerCase();
        var s = Store.create({
            name:name
        });
        s.set('_data', {});
        stores[name] = s;
        return s;
    };

    Demo2App.Stores.get = function (name) {
        name = name.toLowerCase();
        return stores[name];
    };

    Demo2App.Stores.remove = function (name) {
        name = name.toLowerCase();
        delete stores[name];
    };

    Demo2App.Stores.clear = function (name) {
        name = name.toLowerCase();
        stores[name].set('_data', {});
    };

    Demo2App.Stores.find = function (name, id) {
        var ds = Demo2App.Stores.get(name);
        var entity = ds.find(id);
        if (entity) {
            return entity;
        }
        return null;
    }

})();


var personsDS = Demo2App.Stores.create('person');
personsDS.add(Demo2App.Person.create({
    id:"123",
    firstName:"Aaron",
    lastName:"Bourne"
})
);

personsDS.add(Demo2App.Person.create({
    id:"234",
    firstName:"Bonnie",
    lastName:"Highways"
})
);

personsDS.add(Demo2App.Person.create({
    id:"345",
    firstName:"Daddy",
    lastName:"Peacebucks"
})
);

personsDS.add(Demo2App.Person.create({
    id:"456",
    firstName:"Cotton",
    lastName:"Kandi"
})
);


Demo2App.mainController = Ember.Object.create({

    gifts:Ember.ArrayController.create({
        content:[],

        newGift:function (details) {
            var gift = Demo2App.Gift.create(details);
            this.addObject(gift);
            return gift;
        }
    })
});

var giftsView = Ember.View.create({
    templateName:'gifts',
    giftsBinding:'Demo2App.mainController.gifts',
    giftReason:'Birthday 2011'
});

var moreGifts = [
    { name:'Book', excitement:'3', personId:'123' },
    { name:'Shirt', excitement:'1', personId:'234'},
    { name:'Game System', excitement:'5', personId:'123'},
    { name:'Movie', excitement:'4', personId:'345'},
    { name:'Gift Card', excitement:'3', personId:'123'},
    { name:'MP3 Player', excitement:'3', personId:'456'},
    { name:'Tie', excitement:'1', personId:'456'},
    { name:'Candy', excitement:'3', personId:'234'},
    { name:'Coffee', excitement:'3', personId:'123'},
    { name:'Blanket', excitement:'2', personId:'456'},
    { name:'Camera', excitement:'4', personId:'234'},
    { name:'Phone', excitement:'5', personId:'234'},
    { name:'Socks', excitement:'1', personId:'123'},
    { name:'Game', excitement:'5', personId:'456'}
];

var moreGiftsIndex = moreGifts.length;

$(function () {

    function addMoreGifts() {
        moreGiftsIndex--;
        if (moreGiftsIndex >= 0) {
            Demo2App.mainController.gifts.newGift(moreGifts[moreGiftsIndex]);
        }
        setTimeout(addMoreGifts, 2000);
    }

    giftsView.append();
    addMoreGifts();
});

Here’s a few details about the JavaScript code. #1, it’s got very little in the way of error checking. I’ll add that later. And if you use it, you should add it. :)

Recall the simple data model with each gift having a foreign key reference to a person. If you look at each Gift, it contains keys that mirror the DB: name, excitement, and personId. All 3 are handled as strings. The Person class has an Id, lastName, and firstName.

When creating the Ember Gift class, I made a connection to the Person class using a new function I added called entityRef.

Demo2App.Gift = Ember.Object.extend({
    personId:null,
    person:Ember.entityRef('personId') // [3] 
});

As you can see on line [3] above, a person property is declared not with a value, but as a function. Ember.js includes support for computed properties which, when properly used, return a computed value and can be automatically updated when a dependency is established between the function and the values the computed property requires. Following is the code for the entityRef function.

(function () {
    Ember.entityRef = function (property, type) {
        // auto build connection if property is in the form
        // of 'typenameID' if type is not specified
        if (arguments.length === 1 && property) {   // [1]
            var l = property.length;
            if (l > 2 && property.substr(l - 2).toLowerCase() === 'id') {
                type = property.substr(0, l - 2);
            } else {
                throw new Error("Type not specified, and cannot automatically determine store name. Referenced property name must end with Id.");
            }
        }
        var fn = new Function("return Demo2App.Stores.find('" + type + "', this.get('" + property + "')); ");
        return Ember.computed(fn).property(property);
    };
})();

Thankfully, the entityRef function doesn’t need to do much, and much of what it does is make it easy to make a connection automatically between the local data store and the source property by using a simple naming convention.

Starting at [1], the code makes a simple attempt at doing an “auto-map” when a type isn’t specified specifically. In this case, when “personId” is passed to the function, it chops the “Id” off and uses “person” as the name of the data store automatically. It doesn’t validate the name or anything sophisticated (as I said, it’s light on error checking), but it works when everything is specified correctly. In this case, there’s a “person” data store created in the JavaScript code created a little bit later.

var personsDS = Demo2App.Stores.create('person');

Once the data store type name is located, it creates a new function which returns the object by Id stored in the named data store. That function is wrapped in an Ember computed function, and then the dependency on the “personId” is established. Both of these features are core to the Ember JavaScript library (check out the Ember JavaScript library web site for more information). While they may look a bit strange if you’re not familiar with Ember, trust me, they’re perfectly normal. :)

The remaining code is standard Ember JavaScript code with the addition of a simple data store object that manages objects by Id.

Have fun!

(And if you haven’t transitioned from the earlier SproutCore 2.0 betas to Ember, this code should work with a tiny bit of namespace fixing).

Filed under: Coding, General     Tags: EmberJS, SproutCore
1 Comment   

Nest Thermostat Review, Update #1

Posted by Aaron on Tuesday, December 27th 2011    Tweet

After a few weeks of using the Nest thermostat, I’ve got a few more comments that I’d like to share. (Here’s my post about the installation).

The learning feature honestly hasn’t been very useful in the first few weeks. It’s apparently easily confused by days that you’re home unexpectedly (for example, a holiday or vacation). If these days are early in the learning process, it makes some very poor choices as to when to activate the HVAC system. I’d recommend not installing it during periods of very inconsistent schedules for this reason.

image

It doesn’t have a “I’m on vacation today” mode which would be extremely useful and ideally would help while it’s learning (and other days).

In a recent update, Nest made it significantly easier to manage the schedule of a day from the web site – by being able to copy the settings from one day to another:

image

I found the variations in the early learning to be not helpful as we didn’t arrive home at the same time every day, so I mirrored all of the week days for now to better reflect our typical schedules. (And to be clear, the thermostats each reported that they’d “learned” enough to start doing the work automatically before I started making manual adjustments).

I had the expectation that the thermostat would begin to predict when we wanted a specific temperature and start adjusting for it. For example, if we arrive home at 6pm, we want the house to be nearly completely warmed to our preferred temperature (69F) at that time. Not start warming at 6pm. In colder winter months of southern Wisconsin, it takes about 45 minutes to increase the house’s temperature by 9 degrees from the away temperature we’ve set of 60F.

Unfortunately, it doesn’t seem like Nest performs that function. It has the right data – and a simple behavior switch is all it would take. I’d love to see it added. The thermostat already has an estimate of how long it takes to reach a certain temperature, so it could activate the HVAC system more intelligently than traditional programmable thermostats.

So for now, I’ve manually adjusted the schedule to better reflect our requirements. We don’t need too many temperature adjustments during an average day. In fact, most programmable thermostats can meet our needs when it comes to the basic requirement of a scheduled temperature adjustment.

We’ve not used the ‘auto-away’ feature yet successfully. By that I mean the thermostat can detect that you’re not at home and automatically set the temperature to the “away” temperature. One day, it reported auto away when we were still home. I’m not sure why as we’ve got 3 Nest thermostats, one on each floor, and I’m convinced we’d walked in front of one of them very frequently during the day.

I’ve seen this problem more than once with the thermostats:

image

It’s never been the same thermostat, and I’m 100% confident that each of the Nest thermostats is always within a strong WiFi signal.

image

When I noticed the problem this morning (right as I was about to write this blog post), I took a snapshot of the screen and went down to our basement to see if the thermostat was reporting an error. It was not. I went through the settings to see when it had last connected to the “Nest Cloud” and it claimed it had just done that. When I returned to the computer, the web site had updated and did not report any errors. I don’t know what to make of that issue and will continue to watch for patterns to the problem.

The mobile applications are functional. I’ve forgotten we have them though and fail to take advantage of them consistently. Yesterday, we missed an opportunity to remotely adjust the temperature of the home before we arrived after being away for several days in Chicago. It would have been nice to return to a warm home. :-)

I’ve written Nest support once making a few suggestions about their web application – some things that were bugging me. Unfortunately, no human responded (just an automated response). I am disappointed by that. It’s very low effort to paste in a “thanks for your feedback” type of a response and hit send. Nest as a company likely could live and be successful on their technology and devices. But, to thrive, they need awesome customers. Right now, they have not gotten customer service figured out. I also pinged their Twitter account asking for an RSS feed on their blog (seriously! they don’t have one) and they responded they were working on it. I know how hard it is to setup a blog these … WHAT?! They should be scouring the Internet, looking for positive and negative feedback and reacting to it.

I want to be excited about this type of technology. It has promise. Since heating and cooling costs so much these days, I want to be more efficient about how we spend money on heating and cooling and how we use non-renewable resources. The Nest thermostat is most certainly a new way of thinking about the user experience of a normally mundane and ignored device in the home. Having owned a (Radio Thermostat) Filtrete Touch-Screen programmable thermostat with WiFi (on Amazon for around $100), I can attest to the horrible user experience of some of the alternatives.

However at $249 USD each, I remain neutral to negative about this product. While the geek factor is high, and the usability and user experience of the product is very well done, it’s a very expensive thermostat for the home. The Radio Thermostat I mentioned above, while it’s difficult to setup, has most of the same features and is $150 less. The Radio Thermostat is not particularly attractive, but it would be a conversation starter in most homes. The Nest definitely would be.

For less than $50US, it’s easy to obtain a decent programmable thermostat. I’ve bought them many times over the years for various locations, including some apartments we were living in.

Final words of advice/feedback for potential Nest owners now:

If you have a decent programmable thermostat already consider whether it’s worth an additional $250 to:

  • Frequently remotely adjust the temperature of the house
  • Have more than the 5 to 7 daily adjustments you’re allowed by typical programmable thermostats
  • Have a thermostat which could theoretically save you money by detecting you’re not at home (if you have a location for the thermostat which makes it possible to detect you being home/away).
  • Have a glitzy color thermostat that doesn’t show the time on it when you walk by (still missing that feature)
  • Encourage you with a small green leaf to turn up/down the temperature to save you money (yes, it’s weak)
  • Have a topic to talk about with your friends (“Hey! I got a new color thermostat”)

If you already have a decent programmable thermostat and were conscious of when you needed to adjust it (hold) based on unexpected scheduled changes, save your money and wait for something cheaper unless you really need the features above.

I remain very skeptical whether we’ll recoup the costs of the units in energy savings.

If you feel otherwise about the thermostat (or agree), speak up! I’d like to see what the other early adopters think about it. I’ve read some stupidly excited tweets/posts about the product that are often: “OMG! It’s a programmable color thermostat! OMG! Love it!!” Yeah. My phone doesn’t have wires and also has a color screen. Smile

Filed under: General, Recommendations, Usability     Tags: Experience, Issue, Nest, Problem, Review
10 Comments   
Newer Entries »
« Older Entries
Google
Custom Search
Follow @wiredprairie

Archives

  • May 2013 (1)
  • March 2013 (5)
  • February 2013 (2)
  • January 2013 (5)
  • December 2012 (4)
  • October 2012 (2)
  • September 2012 (1)
  • August 2012 (8)
  • July 2012 (2)
  • June 2012 (2)
  • May 2012 (1)
  • April 2012 (9)

Pages

  • About
  • Contact Me
  • Cooking
    • GE Advantium Oven
  • Nest Thermostat
  • Old Archives
  • Photography
  • Quick Searches
  • You need this

Links

  • Old WiredPrairie Archives - Here lies the old WiredPraire content …
  • Quick Searches - A few filtered searches on Google to cut down the signal to noise ratio …
  • SnugUp - Best SmugMug Uploader for Windows

Subscribe

  •  Subscribe in a reader

     Subscribe to comments

    add

Browse Our Tag Archives

.NET Ajax API ASP.NET backbonejs bug C# CSharp Ember.JS Experience Expression Blend Flash Flex hash Html IE8 Internet Explorer Interviews Issue JavaScript knockoutjs LINQ Metro MongoDB MVC Nest Node npm Problem Review Ruby Silverlight SmugMug SnugUp SproutCore SVG TECHED Thermostat Typography Silverlight Flash View Source Windows Windows 8 WinRT WPF Xaml

©2007-2013 WiredPrairie

Disclaimer: All data and information provided on this site is for informational purposes only.

In association with Amazon.

WordPress Themes by Irish Band & Steel Band
(And, quite a few tweaks by WiredPrairie)