org.ambientdynamix.contextplugins.withingsdataplugin.WithingsDataPluginRuntime.java Source code

Java tutorial

Introduction

Here is the source code for org.ambientdynamix.contextplugins.withingsdataplugin.WithingsDataPluginRuntime.java

Source

/*
 * This Plugin is licensed under GNU GPL v3 
 *
 *      http://www.gnu.org/licenses/gpl.html
 *
 *Author: Luaks Ruge
 */
package org.ambientdynamix.contextplugins.withingsdataplugin;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.Vector;

import org.ambientdynamix.contextplugin.api.ContextPluginSettings;
import org.ambientdynamix.contextplugin.api.PluginPowerScheme;
import org.ambientdynamix.contextplugin.api.PushPullContextPluginRuntime;
import org.ambientdynamix.contextplugin.api.security.PrivacyRiskLevel;
import org.ambientdynamix.contextplugin.api.security.SecuredContextInfo;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONObject;

import android.os.Bundle;
import android.util.Log;

/**
 * Bla
 * 
 * @author Lukas Ruge
 */
public class WithingsDataPluginRuntime extends PushPullContextPluginRuntime {
    private final String TAG = this.getClass().getSimpleName();
    private boolean okToRun = true;
    private String sampleData;
    private PluginPowerScheme powerScheme;
    public static final String SAMPLE_DATA_KEY = "SAMPLE_DATA_KEY";
    private static final int EVENT_VALID_MILLS = 86400000;
    private String username = "";
    private String password = "";
    private String email = "";
    private long id = 0;
    private int ispublc = 0;
    private String hash = "";
    private String publicKey = "";
    WithingsUser user = new WithingsUser();
    //private String publicKey="0bbd8d45f59506b95871fcf4fc1f6f981326d00d6cd47b1db5fff4053513 ";

    @Override
    public void init(PluginPowerScheme scheme, ContextPluginSettings settings) throws Exception {
        setPowerScheme(scheme);
        powerScheme = scheme;
        Log.i("Withings", "INIT");
        /*
         * Try to load our settings. Note: init can be called when we're NEW and INITIALIZED (during updates)
         */
        if (loadSettings(settings)) {
            // Since we successfully loaded settings, tell Dynamix we're configured.
            getAndroidFacade().setPluginConfiguredStatus(getSessionId(), true);
        } else {
            // Since failed to load our settings, tell Dynamix we're not configured.
            getAndroidFacade().setPluginConfiguredStatus(getSessionId(), false);
        }
        Log.i("Withings", "init  done");
    }

    /**
      * start the process by loging in, finding the right user and 
      * retreiving the values, then submitting the new values.
      * 
      * the method is  loop that sleeps a while in between.
      * 
      * a new context is only pushed if a new value is found.
      *
      */
    @Override
    public void start() {
        Log.i("Withings", "start()");
        okToRun = true;
        while (okToRun) {
            float[] values = new float[8];
            Log.i("Withings", "oktorun");
            //HERE COMES ALL THE LOGIC
            //only do anything if the user has configurated his username, email and password
            if (!(email.equals("")) && !(username.equals("")) && !(password.equals(""))) {
                Log.i("Withings", "we will try:");
                try {
                    Log.i("Withings", "to log in");
                    login();
                } catch (Exception e) {
                    Log.i("Withings", "but failed");
                    e.printStackTrace();
                }
                try {
                    Log.i("Withings", "to get the userlist");
                    getUsersList();
                } catch (Exception e) {
                    Log.i("Withings", "but failed");
                    e.printStackTrace();
                }
                try {
                    Log.i("Withings", "to get actual values");
                    values = getValues();
                } catch (Exception e) {
                    Log.i("Withings", "but failed");
                    Log.i("Withings", "" + e);
                    e.printStackTrace();
                }
            } else {
                Log.i("Withings", "No username, email or password provided. Plugin can not be used.");
            }
            boolean push = false;
            for (int i = 0; i < values.length; i++) {
                if (values[i] > 0) {
                    Log.i("Withings", "" + values[i]);
                    push = true;
                }
            }
            if (push) {
                Log.i("Withings", "new stuff");
                doPushContextDetection();
            }
            //and some sleep time... this may be rather high since updates to the withings datatabse are probebly just happen about once a day
            try {
                Thread.sleep(300000); //five minutes minute
                if (powerScheme == PluginPowerScheme.BALANCED || powerScheme == PluginPowerScheme.MANUAL) {
                    Thread.sleep(10500000); //severakl hours or so...
                }
                if (powerScheme == PluginPowerScheme.POWER_SAVER) {
                    Thread.sleep(10500000); //three hours
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
      * start the process by loging in, finding the right user and 
      * retreiving the values, then submitting the new values.
      * 
      * the method is  loop that sleeps a while in between.
      * 
      * a new context is only pushed if a new value is found.
      *
      *@return a array with the values (default 0 if no new value is found)
      */
    private float[] getValues() throws Exception {
        Log.i("Withings", "getvalues");
        Date lastupdate = user.getLastUpdate();
        Log.i("Withings", "lastupdate" + lastupdate);
        Date newlastupdate = lastupdate;
        String newlastupdatestring = "";
        String request = "http://wbsapi.withings.net/measure?action=getmeas&userid=" + id + "&publickey="
                + publicKey +
                //"&limit=10"+
                "&devtype=1"; // Only values from the scale
        String response = performRequest(request);
        System.out.println(response);
        // Decode JSon object
        JSONObject json = new JSONObject(response);
        JSONArray JSONvalues = json.getJSONObject("body").getJSONArray("measuregrps");
        JSONObject JSONvalue;
        JSONArray JSONtypes;
        JSONObject JSONtype;
        long timestamp;
        float weight = 0;
        float height = 0;
        float fatFreeMass = 0;
        float fatRatio = 0;
        float fatMassWeight = 0;
        float diastolicBloodPressure = 0;
        float systolicBloodPressure = 0;
        float pulse = 0;
        int size = JSONvalues.length();
        for (int i = 0; i < size; i++) {
            JSONvalue = JSONvalues.getJSONObject(i);
            if (JSONvalue.getInt("category") != 1) {
                continue;
            }
            timestamp = JSONvalue.getLong("date");
            String stamp = "" + timestamp;
            Date newdate = new Date(Long.parseLong(stamp) * 1000);
            System.out.println(timestamp);
            if (newdate.after(newlastupdate)) {
                Log.i("Withings", "newdate " + newdate);
                newlastupdate = newdate;
                newlastupdatestring = stamp;
            }
            if (newdate.after(lastupdate)) {
                //Log.i("Withings", "newdate after lastupdate");
                JSONtypes = JSONvalue.getJSONArray("measures");
                for (int j = 0; j < JSONtypes.length(); j++) {
                    JSONtype = JSONtypes.getJSONObject(j);
                    //1  Weight (kg)
                    //4  Height (meter)
                    //5  Fat Free Mass (kg)
                    //6  Fat Ratio (%)
                    //8  Fat Mass Weight (kg)
                    //9  Diastolic Blood Pressure (mmHg)
                    //10 Systolic Blood Pressure (mmHg)
                    //11 Heart Pulse (bpm)
                    if (JSONtype.getInt("type") == 1) {
                        if (weight == 0) {
                            Log.i("Withings", "" + weight);
                            weight = (float) (JSONtype.getInt("value") * Math.pow(10, JSONtype.getInt("unit")));
                            Log.i("Withings", "" + weight);
                        }
                    }
                    if (JSONtype.getInt("type") == 2) {
                        //not used yet
                    }
                    if (JSONtype.getInt("type") == 3) {
                        //not used yet
                    }
                    if (JSONtype.getInt("type") == 4) {
                        if (height == 0) {
                            height = (float) (JSONtype.getInt("value") * Math.pow(10, JSONtype.getInt("unit")));
                        }
                    }
                    if (JSONtype.getInt("type") == 5) {
                        if (fatFreeMass == 0) {
                            fatFreeMass = (float) (JSONtype.getInt("value")
                                    * Math.pow(10, JSONtype.getInt("unit")));
                        }
                    }
                    if (JSONtype.getInt("type") == 6) {
                        if (fatRatio == 0) {
                            fatRatio = (float) (JSONtype.getInt("value") * Math.pow(10, JSONtype.getInt("unit")));
                        }
                    }
                    if (JSONtype.getInt("type") == 7) {
                        //not used yet
                    }
                    if (JSONtype.getInt("type") == 8) {
                        if (fatMassWeight == 0) {
                            fatMassWeight = (float) (JSONtype.getInt("value")
                                    * Math.pow(10, JSONtype.getInt("unit")));
                        }
                    }
                    if (JSONtype.getInt("type") == 9) {
                        if (diastolicBloodPressure == 0) {
                            diastolicBloodPressure = (float) (JSONtype.getInt("value")
                                    * Math.pow(10, JSONtype.getInt("unit")));
                        }
                    }
                    if (JSONtype.getInt("type") == 10) {
                        if (systolicBloodPressure == 0) {
                            systolicBloodPressure = (float) (JSONtype.getInt("value")
                                    * Math.pow(10, JSONtype.getInt("unit")));
                        }
                    }
                    if (JSONtype.getInt("type") == 11) {
                        if (pulse == 0) {
                            pulse = (float) (JSONtype.getInt("value") * Math.pow(10, JSONtype.getInt("unit")));
                        }
                    }
                }
            }
        }
        request = "http://wbsapi.withings.net/measure?action=getmeas&userid=" + id + "&publickey=" + publicKey
                + "&limit=10" + "&devtype=0"; // Only values from the scale
        response = performRequest(request);
        System.out.println(response);
        json = new JSONObject(response);
        JSONvalues = json.getJSONObject("body").getJSONArray("measuregrps");
        size = JSONvalues.length();
        for (int i = 0; i < size; i++) {
            JSONvalue = JSONvalues.getJSONObject(i);
            if (JSONvalue.getInt("category") != 1) {
                continue;
            }
            timestamp = JSONvalue.getLong("date");
            String stamp = "" + timestamp;
            Date newdate = new Date();
            System.out.println(timestamp);
            try {
                newdate = new Date(Long.parseLong(stamp) * 1000);
                if (newdate.after(newlastupdate)) {
                    Log.i("Withings", "newdate2 " + newdate);
                    newlastupdate = newdate;
                    newlastupdatestring = stamp;
                }
            } catch (Exception ex) {
                ex.getStackTrace();
            }
            if (newdate.after(lastupdate)) {
                JSONtypes = JSONvalue.getJSONArray("measures");
                for (int j = 0; j < JSONtypes.length(); j++) {
                    JSONtype = JSONtypes.getJSONObject(j);
                    if (JSONtype.getInt("type") == 1) {
                        if (weight == 0) {
                            weight = (float) (JSONtype.getInt("value") * Math.pow(10, JSONtype.getInt("unit")));
                        }
                    }
                    if (JSONtype.getInt("type") == 2) {
                        //not used yet
                    }
                    if (JSONtype.getInt("type") == 3) {
                        //not used yet
                    }
                    if (JSONtype.getInt("type") == 4) {
                        if (height == 0) {
                            Log.i("Withings h", "" + height);
                            height = (float) (JSONtype.getInt("value") * Math.pow(10, JSONtype.getInt("unit")));
                            Log.i("Withings h", "" + height);
                        }
                    }
                    if (JSONtype.getInt("type") == 5) {
                        if (fatFreeMass == 0) {
                            fatFreeMass = (float) (JSONtype.getInt("value")
                                    * Math.pow(10, JSONtype.getInt("unit")));
                        }
                    }
                    if (JSONtype.getInt("type") == 6) {
                        if (fatRatio == 0) {
                            fatRatio = (float) (JSONtype.getInt("value") * Math.pow(10, JSONtype.getInt("unit")));
                        }
                    }
                    if (JSONtype.getInt("type") == 7) {
                        //not used yet
                    }
                    if (JSONtype.getInt("type") == 8) {
                        if (fatMassWeight == 0) {
                            fatMassWeight = (float) (JSONtype.getInt("value")
                                    * Math.pow(10, JSONtype.getInt("unit")));
                        }
                    }
                    if (JSONtype.getInt("type") == 9) {
                        if (diastolicBloodPressure == 0) {
                            diastolicBloodPressure = (float) (JSONtype.getInt("value")
                                    * Math.pow(10, JSONtype.getInt("unit")));
                        }
                    }
                    if (JSONtype.getInt("type") == 10) {
                        if (systolicBloodPressure == 0) {
                            systolicBloodPressure = (float) (JSONtype.getInt("value")
                                    * Math.pow(10, JSONtype.getInt("unit")));
                        }
                    }
                    if (JSONtype.getInt("type") == 11) {
                        if (pulse == 0) {
                            pulse = (float) (JSONtype.getInt("value") * Math.pow(10, JSONtype.getInt("unit")));
                        }
                    }
                }
            }
        }
        float[] values = new float[8];
        values[0] = weight;
        values[1] = height;
        values[2] = fatFreeMass;
        values[3] = fatRatio;
        values[4] = fatMassWeight;
        values[5] = diastolicBloodPressure;
        values[6] = systolicBloodPressure;
        values[7] = pulse;
        Log.i("Withings", "Als neuer Timestamp wird bergeben:" + newlastupdatestring);
        user.update(newlastupdatestring, values);
        return values;
    }

    /**
      * this method handles the login into the Withings plattform
      * 
      * This code is based on the code found at http://code.google.com/p/libra-android/
      *
      */
    private void login() throws Exception {
        // Step one: Get a one-time "magic string"
        String magicString = getMagicString();
        System.out.println(magicString);

        //Step two: Get the MD5 hash of the password
        String passwordHash = md5Hash(password);
        System.out.println(passwordHash);

        //Step three: Concatenate the email, password hash and magic string, separated by a colon
        String concat = email + ":" + passwordHash + ":" + magicString;

        // Step four: Get the MD5 hash of the above string
        hash = md5Hash(concat);
        System.out.println(hash);
    }

    /**
      * this method handles the User List from the Withings platform
      * 
      * This code is based on the code found at http://code.google.com/p/libra-android/
      *
      */
    private void getUsersList() throws Exception {
        String request = "http://wbsapi.withings.net/account?action=getuserslist&email=" + email + "&hash=" + hash;
        String response = performRequest(request);

        // Decode JSon object
        JSONObject json = new JSONObject(response);
        JSONArray JSONusers = json.getJSONObject("body").getJSONArray("users");
        JSONObject JSONuser;
        int size = JSONusers.length();
        for (int i = 0; i < size; i++) {
            JSONuser = JSONusers.getJSONObject(i);
            String theusername = JSONuser.getString("firstname") + " " + JSONuser.getString("lastname");
            if (theusername.equals(username)) {
                id = JSONuser.getLong("id");
                publicKey = JSONuser.getString("publickey");
                ispublc = JSONuser.getInt("ispublic");
            }
        }
    }

    /**
      * this method performs requests to the Withings API
      * 
      * This code is based on the code found at http://code.google.com/p/libra-android/
      *
      */
    private String performRequest(String url) throws Exception {
        // Perform the request
        HttpClient client = new DefaultHttpClient();
        HttpGet get = new HttpGet(url);
        HttpResponse response = client.execute(get);

        // Translate the response into a string
        String result = "";
        try {
            InputStream in = response.getEntity().getContent();
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
            StringBuilder str = new StringBuilder();
            String line = null;
            while ((line = reader.readLine()) != null) {
                str.append(line + "\n");
            }
            in.close();
            result = str.toString();
        } catch (Exception ex) {
            result = "Error";
        }

        // Decode JSon object
        JSONObject json = new JSONObject(result);
        // Make sure that the request was successful
        if (json.getInt("status") != 0) {
            throw new Exception("Withings error: " + json.getInt("status"));
        }

        return result;
    }

    /**
      * this method returns an md5Hash of a String
      * 
      * This code is based on the code found at http://code.google.com/p/libra-android/
      *
      *@param A String to be hashed
      */
    private String md5Hash(String string) throws Exception {
        MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
        digest.update(string.getBytes());
        byte messageDigest[] = digest.digest();

        // Get the Hex String
        BigInteger bigInt = new BigInteger(1, messageDigest);
        return bigInt.toString(16);
    }

    /**
      * this method requests the so called magic String from the Withings API
      * 
      * This code is based on the code found at http://code.google.com/p/libra-android/
      *
      */
    private String getMagicString() throws Exception {
        String request = "http://wbsapi.withings.net/once?action=get";
        String response = performRequest(request);

        // Decode the magic string from the JSon response
        JSONObject json = new JSONObject(response);
        return json.getJSONObject("body").getString("once");
    }

    @Override
    public void handleContextRequest(UUID requestId, String contextDataType) {
        /*
         * Perform context scan without configuration.
         */
        Log.i(TAG, "handleContextRequest for requestId: " + requestId);
        pullEventHelper("", requestId, EVENT_VALID_MILLS);
    }

    @Override
    public void handleConfiguredContextRequest(UUID requestId, String contextInfoType, Bundle scanConfig) {
        /*
         * Use the incoming scanConfig Bundle to control how we perform the context scan.
         */
        Log.i(TAG, "handleConfiguredContextRequest for requestId: " + requestId);
        pullEventHelper("", requestId, EVENT_VALID_MILLS);
    }

    @Override
    public void stop() {
        okToRun = false;
        Thread t = Thread.currentThread();
        t.interrupt();
        Log.d(TAG, "Stopped!");
        Log.d("Withings", "Stopped!111");
    }

    @Override
    public void destroy() {
        stop();
        Log.i(TAG, this + " is Destroyed!");
    }

    @Override
    public void doManualContextScan() {
        pushEventHelper("", EVENT_VALID_MILLS);
    }

    @Override
    public void updateSettings(ContextPluginSettings settings) {
        if (loadSettings(settings)) {
            getAndroidFacade().storeContextPluginSettings(getSessionId(), settings);
            getAndroidFacade().setPluginConfiguredStatus(getSessionId(), true);
        }
    }

    @Override
    public void setPowerScheme(PluginPowerScheme scheme) {
        Log.i(TAG, "Setting PowerScheme " + scheme);
        powerScheme = scheme;
    }

    /*
     * Simple context detection loop that generates push events.
     */
    private void doPushContextDetection() {
        Log.i("Muhaha", "Entering doPushContextDetection");
        // Send a sample broadcast event
        pushEventHelper("", EVENT_VALID_MILLS);
        Log.i("Muhaha", "Exiting doPushContextDetection");
    }

    /*
      * Utility for responding to pull requests.
      */
    private void pullEventHelper(String message, UUID requestId, int validMills) {
        sendContextEvent(requestId, constructEventList(message), validMills);
    }

    /*
     * Utility for sending push events.
     */
    private void pushEventHelper(String message, int validMills) {
        sendBroadcastContextEvent(constructEventList(message), validMills);
    }

    /*
     * Utility that constructs a list of SecuredContextInfo containing each different FidelityLevel.
     */
    private List<SecuredContextInfo> constructEventList(String message) {
        List<SecuredContextInfo> eventList = new Vector<SecuredContextInfo>();
        eventList.add(new SecuredContextInfo(new WithingsDataPluginContextinfo(user), PrivacyRiskLevel.LOW));
        return eventList;
    }

    /*
     * Utility for loading settings.
     */
    private boolean loadSettings(ContextPluginSettings settings) {
        // Check settings type and store
        if (settings != null) {
            Log.i(TAG, "Received previously stored settings: " + settings);
            try {
                sampleData = settings.get(SAMPLE_DATA_KEY);
                username = settings.get("username");
                email = settings.get("email");
                password = settings.get("password");

                return true;
            } catch (Exception e) {
                Log.w(TAG, "Failed to parse settings: " + e.getMessage());
            }
        } else if (settings == null) {
            // Create default settings
            Log.i(TAG, "No settings found!");
        }
        return false;
    }
}