AngularJS and Primus, a perfect couple

Some time ago I shut down my old Home Automation system and the current one is doing just fine. All User Interfaces have had their updates and are working better than before after I started using Primus. Now the time has come to give my website a face-lift.

And as the title of this post suggests, the combination of AngularJS and Primus seemed like a good choice to accomplish that. But first I’d like to see it working with my own data – closer to reality, without the data that has to be displayed defined inside the Controller but preferably delivered by Primus with my Home Automation system as source.

I’m using Primus for a couple of months now and it’s working great. An example of that is a very cheap Android tablet that’s located on the 2nd floor as a User Interface (UI) to control the usual stuff like the roller shutters, lights, front door and security system from there. This tablet loses its Wifi connection about 2 or 3 times a week resulting in a disconnected websocket and hence all the buttons on the UI are ‘dead’ when this happens. Refreshing the page brings back the websocket connection of course, but it was annoying having to do that. Since I implemented Primus, its built-in reconnect feature makes this tablet ready for use 24/7 without having to refresh. Cool. Couldn’t have done it better ๐Ÿ˜‰

I also switched to another reverse proxy in the process. This used to be Apache running on a Linux VM but since a week or so I’m using nginx, currently running on a Raspberry Pi.

On to AngularJS. AngularJSย “lets you extend HTML vocabulary for your application” as the website says. It came to my attention in the summer of 2013 and it has been on the to-do list ever since. I saw some examples and immediately knew I had to learn how to use it.

The last couple of days I tried to do so. After initially ‘wrestling’ with some new terminology like Controllers, Services, Providers and Directives I bought the ng-book and made my first (almost) self-made web-page. Great.

But as already mentioned above, I wanted to see Primus and AngularJS working with my data and I wanted to see some ‘building blocks’ (like grids, charts, labels) of my website being turned into dynamically updating components – without any refresh triggered by a button or time interval. Yuck… what I see, must always be the latest information available.

Now all I needed was some way to make Primus, the real-time data transporter and AngularJS cooperate. For that I found angular-primus. And I had some extra demands: I should be able to create a chart ‘pre-loaded’ with the history of x minutes/hours and grids should also contain all the items right away. And I should be able to highlight changing values to may them more noticeable. And ….

After fiddling with Angular, Primus, Providers, Directives, Controllers for a couple of evenings I came up with this (click the image to go to the live web-site)ย :


Brilliant… every value displayed is being updated automagically – the Smart Meter data, the line chart (with the help of HighCharts, BTW), the Temperature column values in the upper grid and new events being added to the lower grid. Just take a peek and see everything changing & moving.. just what I always wanted! The first item I built was the hardest, the ones after that were done much quicker.

Now that I finally see what AngularJS and Primus can do with my data, I think it’s safe to say that those 2 make a perfect couple for me!



Tagged , , , . Bookmark the permalink.

10 Responses to AngularJS and Primus, a perfect couple

  1. Kylix says:

    Hi Robert! It’s very neat! Would it be possible to have the code used? I’m trying to get the same result displaying a few sensor values that I get querying an MySQL database.


  2. Patrick says:

    Hoi Robert, nette uitwerking zeg!
    Zelf wil ik iets soortgelijks gaan bouwen om te koppelen aan mijn DomotiGa installatie.
    Tijdens mijn zoektoch naar info op jouw site terecht gekomen, wat een bron van informatie :).
    Zou je in de toekomst misschien wat meer over AngularJS willen bloggen? Zo te zien is dat ook de weg die ik moet gaan volgen.

    Bedankt voor de info op deze site!


    • Hoi Patrick,
      Dank je. Ik kan je helaas niks beloven voor wat betreft de onderwerpen die in de nabije toekomst voorbij zullen komen, want daar zit totaal geen ‘structuur’ in – ze ontstaan spontaan, door datgene wat me op enig moment bezig houdt – en dat wil nogal eens veranderen ๐Ÿ˜‰ Maar dat AngularJS vaker aan bod zal komen staat wel vast.

      • Patrick says:

        Hoi Robert,
        Bedankt voor de ‘cliffhanger’ ๐Ÿ˜‰ !
        Het hele HTML5, responsive, node.js verhaal is behoorlijk nieuw voor mij.
        Tot nu toe heb ik alles met iViewer gedaan, hier zat het nodige javascript in maar is toch anders dan HTML. Door de beperkingen (en dure licenties) ben ik gaan zoeken naar een alles-in-1 oplossing. Blijkbaar dacht jij hetzelfde ๐Ÿ™‚
        Zou ik je in de toekomst af en toe een vraag omtrent dit alles kunnen stellen (als ik vast loop in de materie)?

        • Tuurlijk. Maar voor mij is veel ook een kwestie van learning on the job, dus ik heb vast niet overal een antwoord op. Je mag me ook rechtstreeks mailen, ik denk dat je mijn email adres wel kunt bedenken ๐Ÿ˜‰

  3. Rene says:

    Hi Robert,

    This “pre-loaded data”, is that handled by Primus/AngularJS, or did you fix that using own code?
    Problem with MQTT is that you can only retain the last value, while I would like to see the last 10 or 20 values for example. I don’t think MQTT has a way to do this?


    • I wrote some code for that – since it’s historical data, I have to retrieve it from the database, which is done in the node script like this, for example the last 60 samples of the current power usage:
      app.get('/pwrcurr', function(req, res){
      var query = db.query("select UNIX_TIMESTAMP(time) as timestamp, value, time from data where deviceid='smartmeter.pwrcurr' order by time desc limit 60;");
      var result = [];

      query.on('result', function(row, index) {
      var x = row['timestamp'] * 1000;
      var y = row['value'];
      result.unshift(new Array(x,y));
      .on('end', function() {
      // all rows have been received
      res.send({data : result});

      This is way too static of course, but for this stage enough to see if it worked as I expected.

      • Rene says:

        Hi Robert,

        A bit late, but thanks for you reply.

        Checking your latest webpage ( I noticed you send historical data and mqtt updates. To distinguish this data you seem to use a data.type field.

        Questions: who fills in this type field? Your node app? Also, how do you make sure the historical data is process first by highcharts? Where did you find all the info to make this awesome webpage?


        • Who fills in this type field? Your node app?
          Yep. The server-side node script receives MQTT information and it can also query the database for historical data.
          The way in which the script gathers the information determines its data.type.

          Also, how do you make sure the historical data is process first by highcharts?
          This is done in code, like this:

          primus.on('connection', function connection(spark) {





          The upload* function calls write the historical data for the charts to the new spark.
          The last call writes all the (buffered) device values to the spark.
          After this initial upload new MQTT information will be received by a primus.write(), like this:

          MQTTclient.on('message', function(topic, message) {
          var p = {};
          p.type = 'data';
          p.topic = topic;
          p.payload = message;

          Where did you find all the info to make this awesome webpage?
          Here, there, everywhere … no particular website or book.

  4. Rene says:

    Thanks Robert.

    Brilliant way to use a Node client to intercept the MQTT events and forwarding them in a custom format to the web client to show historical data. Primus is indeed the way to go here.

Leave a Reply

Your email address will not be published. Required fields are marked *