Create an app in Ionic Framework

Hi, I describe here how I wrote my first hybrid app in Ionic for the Particle Cloud:

enjoy!
Gustavo.

4 Likes

I checked it out, thanks for sharing.

I have a Photon sending data ever 30 seconds to an Azure Table Database now, and it’s been running for a week now without problems.

I have been reading about Power BI Embedded and from what I have seen you could easily embed the Power BI charts and reports into an Ionic app if you ever wanted or needed to integrate a nice data layout into the mobile app without much effort.

It’s nice to see you have played how to get data into Ionic and show us how to call Particle functions from the phone or tablet.

oh that sounds like a nice experiment to do! do you mind showing me how you entered data in an Azure DB?
Then I can try adding a power BI chart to the app.
Thanks
Gustavo.

Yea no problem.

Are you starting from scratch or have you followed the instructions in this tutorial?

I followed that tutorial but not all the way since I did not setup the Connect The Dot’s website since I was just interested in getting data from the Photon into the Azure Database so I could then access the data in Power BI.

If all you want to do is get Azure to receive the Webhook send from a Photon / Electron then all the steps they walk you though in the tutorial is overkill, it does teach you a lot you would not learn otherwise.

I would recommend following the tutorial so that you understand how to send a receive data and use Stream Analytics to process the incoming data.

Paul explains a lot in the first video about how all this works so be sure to watch that a few times.

Then in the 2nd video on the tutorial page, they go over using a script to setup all the Azure accounts automatically, but I recommend you do not use the script but do it manually so you’ll understand what is going on better.

Instead follow this walk through and do it manually while using the 2nd video as an extra guide where they talk about what is happening in every setup of the configuration process.

Then once you have that setup and see it working you can stop the Stream Analytics job you created to prevent it from wasting money. Azure offers new users a 30-day free trial with $200 in credit for all their services. The Stream Analytics job used in the example was costing me like five bucks a day when sending a webhook every 30 seconds.

Sending a full sized JSON payload to an Azure Table Database 2880 times (every 30 seconds) over 24 hours is costing me about 77 cents a day of that free credit. I do not need that resolution, so I expect to log every 3-5 mins with an expected monthly cost of $5.

So once you have the tutorial finished, you can follow this blog post on how to setup a very simple Stream Analytics Job to push webhook received from an Event Hub directly the Azure Table Database.

All you really need to focus on in the guide below is to create the Steam Analytics job since that is what is actually doing the job of pushing the incoming JSON data into the Azure Table.

http://sarkar.io/iot/2016/01/13/azure-iothub-table-store/

Let me know if you have any questions and good luck :smiley:

1 Like

oh wow, you did quite a lot of reading! thank you for the directions.

For the dashboard, I just learned that in the Ionic app you can add an iframe. You configure the iframe with the public link of your dashboard and voila!

Here’s the html code:
(source)

<ion-content scroll="true" overflow-scroll="true" class="iframe-wrapper">
   <iframe data-tap-disabled="true" src="{{DASHBOARD_URL}}"></iframe>
</ion-content>

And here’s a very rough preview of an Ubidots dashboard I had, now displayed inside the Ionic app:

Cheers,
Gustavo.

Sweet. The Power BI dashboard will provide an iFrame code snippet to display your custom dashboard so it will work the same as your Ubidots example. Super simple.

I just checked out the http://ionic.io website and it looks like a killer solution.

Do you plan on creating an app that you will submit to Android & iOS? I wonder how painless that is or isn’t.

Learning how to get the data over to Azure did take some reading but once you do it once the next time only takes a few mins.

I added an Electron to the Azure Event Hub along with the Photon last night, and it took me like ten mins to get it done while referencing the tutorial to make sure I got it right. Once you know what’s up you can add devices in just a few mins.

The Electron just sends a Webhook to the Azure Event Hub, and it’s JSON data is pushed into the same Azure Table Database with its unique Product ID. So you just end up with one large database or all your devices that you can access as needed.

The Azure Table Database allows you to set how long you want to store past data before it’s automatically deleted. I chose to set the data retention time to 90 days so that any sensor data older than 90 days automatically get’s deleted. You can turn this feature off if you desire.

If you want an easy way to get your data into Power BI, you can just tie one of your Google Sheet documents your logging to as a Datasource and then create charts or graphs to look as you please.

I spent the day creating a Wordpress website with the ability to have a custom client portal where my customers can create an account. Once logged in they will be taken to a private web page that will have an embedded Power BI dashboard displaying all the live data that is being generated by their particular product.

I’m hoping that the site renders well on PC web browsers, Phone, and Tablets so I will not need to create custom-branded Android & iOS application also. If I do need to create the custom apps your work so far will for sure help me get started quicker.

In about 30 mins today I had a custom domain and a WordPress site up and running. I then added the Divi theme as a simple way to make it look good; it’s just a template now.

Then I added this plugin called WP-Client which creates an excellent custom user portal, so each user has their custom private web page where I plan on giving them a Power BI dashboards for their connected products. It looks like it’s going to work out pretty good this way which is nice because it’s cheap and easy to setup :smiley:

It’s just a template right now, but at least I have a working site and Client Log-In portal running. www.PortableSolarPower.io

Yes, I plan on trying to publish an app for any of my projects just for the fun of it and for checking out how hard or easy publishing an app is - I will also brag about it with my friends, of course :smile:

awesome work on your site! Let me know when is up and running and I can log in so to check it out more in detail. the Divi plugin looks pretty nice.
are you hosting all this in Azure?
thanks,
Gustavo.

Here is a AngularJS factory that you can use in your controllers (chained promises required) that eliminates the need to hardcode the token so your app doesn’t break when your token expires. I am looking at using Keen.io for my stream analytics and D3 for charting in Ionic. I haven’t added functions or variables yet but this is a good start built on the work of @thebelin ClientID and Pass is Particle:Particle

/**
 * A service for operating localStorage as a service in angular.js
 */
appServices.service('localstorage', function() {
  // Set this to false to suppress console messages
  var debug = "localstorage service ";
  // Store this data
  this.setItem = function (key, data) {
    if (debug) console.log(debug + 'setItem %s', key, data);
    return window.localStorage.setItem(key, JSON.stringify(data));
  };

  this.getItem = function (key) {
    var ret = JSON.parse(window.localStorage.getItem(key));
    if (debug) console.log(debug + 'getItem %s', key, ret);
    return ret;
  };

  this.removeItem = function (key) {
    if (debug) console.log(debug + 'removeItem %s', key);
    return window.localStorage.removeItem(key);
  };

});


appServices.factory('pDevices', function($http, localstorage) {
  //Factory Variables and Private Methods
  root_url = 'https://api.particle.io/v1/',
  auth_url = 'https://api.particle.io/oauth/token',
  clientID_pass = 'Basic cGFydGljbGU6cGFydGljbGU=',
  device_id = localstorage.getItem('device_id'),
  access_token = localstorage.getItem('access_token');

  var login = function (username, password) {
    return $http({
        method: 'POST',
        url: auth_url,
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
          'Authorization': clientID_pass // particle:particle
        },
        transformRequest: function(obj) {
          var str = [];
          for(var p in obj)
            str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
          return str.join("&");
        },
        data: {grant_type:'password', username: username, password: password}
    }).then(
      function (response) {
      // The response should be an object with an access token
      console.log("login.then returning: ", response);
      setToken(response.access_token);
      return response.data;
      }
    ).catch(
      function(response) {
        console.log('Login attempt failed');
        return $q.reject(response);
      }
    );
  };

  var logout = function () {
    setToken(-1);
    setDevice(-1);
    $http.defaults.headers.common.Authorization = '';
  };

  //List Devices claimed by the authenticated user/access_token
  //Returns a chained promise from angular $http
  var getDevices = function() {
    return $http.get(root_url + 'devices').then(
      function(response) {
      console.log('Particle Devices : ', response.data);
      return response.data;
      }
    ).catch(
      function(response) {
        console.log('Get Devices API Call Failed with HTTP Status: ' ,response.status);
        console.log('Status Text from failed Call: ', response.statusText);
        return $q.reject(response);
      });
    };

  /*Set the device as selected via deviceId from the pDevices Object.id.  Store the
  Object.id as device_id in localstorage. NOTE: localstorage is not a secure/
  production ready method of storing tokens or deviceIds.  For demo purposes only.
  If no deviceID is passed it will return device_id from localstorage or null if
  none stored.*/

  var setDevice = function(deviceId){
    if (deviceId) {
      if (deviceId === -1) deviceId = '';
      console.log('setpDevice', deviceId);
      device_id = deviceId;
      // Also store in localstorage
      localstorage.setItem('device_id', device_id);
    }
    return device_id;
  };

  var callFunction = function (functionName, command, callback) {
    $http.post(root_url + 'devices/' + device_id + '/' + functionName,
    {arg: command}).then(
      function (response) {
      // particle call success
      console.log('particle %s %s return success', functionName, command, response);
    }
  ).catch(
    function (response) {
      console.log('fail on call data');
    });
  };

/*          Private/Helper Functions        */
  var setToken = function (newToken) {
    if (newToken) {
      if (newToken === -1) newToken = '';
      console.log('set Token: ', newToken);
      access_token = newToken;
      $http.defaults.headers.common.Authorization = 'Bearer ' + access_token;
      // Also store the access token retrieved
      localstorage.setItem('access_token', access_token);
    }
    $http.defaults.headers.common.Authorization = 'Bearer ' + access_token;
    return access_token;
  };

  return {
    login: login,
    logout: logout,
    getDevices: getDevices,
    setDevice: setDevice,
    callFunction: callFunction,
    setToken: setToken,
    };
});

My login controller so you have an example of how to use the factory.

// Controller of login page.
appControllers.controller('loginCtrl', function ($scope,$state,$stateParams,$ionicHistory,pDevices,safeparse) {
    //$scope.isAnimated is the variable that use for receive object data from state params.
    //For enable/disable row animation.
    $scope.isAnimated =  $stateParams.isAnimated;
    var debug = true;
    $scope.p = {
      username: '',
      password: '',
      rememberMe: false,
      currentDevice: '',
      deviceId: pDevices.setDevice(),
      token: pDevices.setToken(),
      devices: [],


  // Authenticate on the particle.io platform
      authenticate: function () {
        pDevices.login($scope.p.username, $scope.p.password).then(
          function(authData) {
          if (debug) console.log(debug + 'authenticated: ', authData);
          $scope.p.token = authData.access_token;
          pDevices.setToken($scope.p.token);
          $state.go('app.devices');
        }).catch(
          function(authData) {
          if (debug) console.log(debug + 'authenticated: ', authData);
          $scope.p.token = '';
          pDevices.setToken($scope.p.token);
        });
      },

      // terminate session and blank the token
      logout: function () {
        $scope.p.username = '';
        $scope.p.password = '';
        $scope.p.rememberMe = false;
        $scope.p.token = '';
        $scope.p.devices = [];
        pDevices.logout();
      },

    };
});// End of controller login.
1 Like

oh, thank you for the code, @LukeUSMC!
I’ll take a look at how to use it.

What did you mean by this text below?

Again, thanks,
Gustavo.

If you are working on a product you would replace that with your OAuth creds (shouldn’t be in the app when published as hybrid apps are easily decompiled, treat like a web client per docs). When using your personal account you can use particle:particle as user/pass encoded in Base64 https://docs.particle.io/reference/api/#generate-an-access-token

Each time you login you will generate a new token so best practice would be to check local storage for an existing token and use an http-interceptor (well documented) to catch expired tokens. If a token exists then use $state.go(‘app.NOT_login’) if not then $state.go(‘app.login’) an interceptor will catch any http status of 400 (failure) and redirect your user to the login page so they can generate a new token and that would be stored to localstorage again (not exactly secure but fine for testing/learning). My plan for an app that can be published and secure is to setup a webservice to encrypt and decrypt tokens and keys on demand. Keys more than tokens because exposure of a key means a malicious user can impersonate your APP.

oh wow, thanks for the reply. Looks like I have a lot of reading to do! :grimacing:

The WordPress site is hosted with GoDaddy on their WordPress platform which gets’s setup automatically in 5 mins. It’s only $9.95 a month.

The Divi theme was $59, and the WP-Client plugin was another $89. I consider that cheap for what it is doing.

1 Like