Completed Projects

Current Projects

Garage a Trois

Parts:
My server didn't have an onboard printer port. so I had to buy a parallel PCI card with a moschip controller - there are a lot of fake unsupported parellel adapters out there.
Relay controller board (USB or Parallel) - $40 (wow)
Android device
LAMP Server

Design:
The server controls the relay via parallel port (in my case)
note: I got everything physical up and running using http://www.faqs.org/docs/Linux-mini/IO-Port-Programming.html as a guide, thank you Tomi Engdahl!

Android app controls relay by sending POST to garage_a_trois.php script running on the server.

garage_a_trois.php Code:


<?php
//file_put_contents("post.log",print_r($_POST,true));
if (!isset($_POST['UID']))
{
        echo "No user";
        exit;
}
if (in_array($disallowed_DIDs, $_POST['DID']))
	echo "Bad device";
	exit;
$allowed_users = Array(
        0594 => "Test",
        0000 => "Test2"
	//prevent duplicate PIN assignment by using PIN as array key.
        );
$disallowed_DIDs = Array(
	0 => 'ek2j234kb88akk'
	//if someone is trying to hack you put their DID here :)
        );

if (isset($_POST['g_switch'])){
        if ($_POST['switch'] == "g_light"){
                shell_exec("/usr/local/sbin/portcontrol LPT1DATA read setbit 4 write");
                sleep(2);
                shell_exec("/usr/local/sbin/portcontrol LPT1DATA read resetbit 4 write");
                echo 'Light toggled';
        }
        if ($_POST['g_switch'] == "g_door"){
                shell_exec("/usr/local/sbin/portcontrol LPT1DATA read setbit 5 write");
                sleep(2);
                shell_exec("/usr/local/sbin/portcontrol LPT1DATA read resetbit 5 write");
                echo 'Door toggled';
        }
        if ($_POST['g_switch'] == "g_lock"){
                shell_exec("/usr/local/sbin/portcontrol LPT1DATA read setbit 6 write");
                sleep(2);
                shell_exec("/usr/local/sbin/portcontrol LPT1DATA read resetbit 6 write");
                echo 'Lock toggled';
        }
	//do some logging

        $myFile = "auth.log";
        $fh = fopen($myFile, 'a');
        $stringData = date("Y-m-d H:i:s") . " ";
        fwrite($fh, $stringData);
        $stringData = $_POST['switch'] . " toggled\n";
        $txt = $allowed_users[$_POST['UID']] . " toggled " . $_POST['switch'];
        // Send email
        mailer("your email address here",$stringData,$txt);
        fwrite($fh, $stringData);
        fclose($fh);
        exit;
}
else{
        if (!isset($_POST['UID']))
        {
                echo 'Log in';
                exit;
        }
        if (array_key_exists($_POST['UID'], $allowed_users)){
                $granted = 'Granted';
                echo 'SomethingAwesomeForTheAppToRecognize';
        }
        else{
                $granted = 'Denied';
                echo 'Access denied';
        }
        $myFile = "auth.log";
        $fh = fopen($myFile, 'a');
        $stringData = date("Y-m-d H:i:s") . " ";
        fwrite($fh, $stringData);
        $stringData = $allowed_users[$_POST['UID']] . ' from ' . $_POST['DID'] . "\n";
        mailer("your mobile email address here 3349392343@vtext.com", "User " . $granted,$stringData);
        fwrite($fh, $stringData);
        fclose($fh);

}
function mailer($to, $subject, $message){
        $from = "notifications@yourserver.com";
        $headers = "From: $from\r\n" . "X-Mailer: php";
        mail($to, $subject, $message, $headers, '-r ' . $from);
}
?>


Android App Code:

note: I called the app Garage a Trois due to the openers three buttons, light, lock and door. /clever
My external opener is fingerprint authentication but most garages come standard with 4 digit PINs as a standard so we'll follow that "security" model.
There are five files in the project we'll go over.

login.xml - the xml file for Login.java
control.xml - the xml file for Control.java
Login.java - the authentication activity
Control.java - the command issuer.
AndroidManifest.xml - tells the app what permissions to use and what orientation to allow

login.xml Code:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/TextView01"
        android:text="@string/pin_entry"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textAlignment="center"
        android:gravity="center"
        android:layout_marginTop="17dp" />

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

        android:layout_width="wrap_content"
        android:layout_height="283dp"
        android:paddingLeft="16dp"
        android:paddingRight="16dp"
        android:orientation="horizontal"
        android:layout_gravity="center"
        android:layout_marginTop="9dp"
        android:layout_weight="0">

        <NumberPicker
            android:id="@+id/numberPicker1"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="64dp" />

        <NumberPicker
            android:id="@+id/numberPicker2"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="64dp" />

        <NumberPicker
            android:id="@+id/numberPicker3"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="64dp" />
        <NumberPicker
            android:id="@+id/numberPicker4"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="64dp" />

    </LinearLayout>

    <Button
        android:id="@+id/readWebpage"
        android:layout_weight="0"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="@string/log_in"
        android:layout_gravity="center_horizontal"
        android:layout_width="fill_parent"
        android:layout_marginLeft="30dp"
        android:layout_marginRight="30dp"
        android:layout_marginTop="-6dp">
    </Button>

</LinearLayout>


control.xml Code:


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_alignParentLeft="true">
        <TextView
            android:id="@+id/textView02"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="@string/placeholder" />
        <ProgressBar
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:id="@+id/progressBar"
            android:layout_gravity="center"
            android:visibility="visible" />
        <View
            android:layout_width="1dp"
            android:layout_height="2dp">
        </View>
        <ToggleButton
            android:id="@+id/button3"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:textOn="Light"
            android:textOff="Light" />
        <View
            android:layout_width="1dp"
            android:layout_height="2dp">
        </View>
        <ToggleButton
            android:id="@+id/button4"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:textOn="Lock"
            android:textOff="Lock" />
        <View
            android:layout_width="1dp"
            android:layout_height="2dp">
        </View>
        <ToggleButton
            android:id="@+id/button2"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:textOn="Door"
            android:textOff="Door"
            android:layout_centerHorizontal="true" />
    </LinearLayout>

</RelativeLayout>

Login.java Code:


package com.airlim.garageatrois;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.Settings.Secure;
import android.util.Log;
import android.view.View;
import android.widget.NumberPicker;
import android.widget.TextView;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class Login extends Activity implements NumberPicker.OnValueChangeListener{
    private TextView textView;
    volatile String a, a2, a3, a4 = "0";
    volatile String authd = "false";
    public void onBackPressed() {
        finish();
    }
    //volatile Handler handler = new Handler();
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.login);
        textView = (TextView) findViewById(R.id.TextView01);
        final NumberPicker np = (NumberPicker) findViewById(R.id.numberPicker1);
        final NumberPicker np2 = (NumberPicker) findViewById(R.id.numberPicker2);
        final NumberPicker np3 = (NumberPicker) findViewById(R.id.numberPicker3);
        final NumberPicker np4 = (NumberPicker) findViewById(R.id.numberPicker4);
        np.setMaxValue(9); // max value 100
        np.setMinValue(0);   // min value 0
        np.setWrapSelectorWheel(true);
        np.setOnValueChangedListener(this);
        np2.setMaxValue(9); // max value 100
        np2.setMinValue(0);   // min value 0
        np2.setWrapSelectorWheel(true);
        np2.setOnValueChangedListener(this);
        np3.setMaxValue(9); // max value 100
        np3.setMinValue(0);   // min value 0
        np3.setWrapSelectorWheel(true);
        np3.setOnValueChangedListener(this);
        np4.setMaxValue(9); // max value 100
        np4.setMinValue(0);   // min value 0
        np4.setWrapSelectorWheel(true);
        np4.setOnValueChangedListener(this);
        np.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
            @Override
            public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
                a = String.valueOf(np.getValue());
            }
        });
        np2.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
            @Override
            public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
                a2 = String.valueOf(np2.getValue());
            }
        });
        np3.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
            @Override
            public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
                a3 = String.valueOf(np3.getValue());
            }
        });
        np4.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
            @Override
            public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
                a4 = String.valueOf(np4.getValue());
            }
        });
    }
    @Override
    public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
        Log.i(picker + "value is", "" + newVal);
    }

    private class LogInTask extends AsyncTask<String, String, String> {
        volatile String android_id = Secure.getString(getContentResolver(), Secure.ANDROID_ID);
        protected String doInBackground(String... urls) {
	    String server = "URL for the php script."

            String response = "";
            for (String url : urls) {
                try {
                    HttpClient client = new DefaultHttpClient();
                    HttpPost httpPOST = new HttpPost("server");
                    List<NameValuePair> params = new ArrayList<NameValuePair>();
                    params.add(new BasicNameValuePair("DID", android_id));
                    params.add(new BasicNameValuePair("UID", url));
                    UrlEncodedFormEntity ent = new UrlEncodedFormEntity(params,HTTP.UTF_8);
                    httpPOST.setEntity(ent);
                    HttpResponse execute = client.execute(httpPOST);
                    InputStream content = execute.getEntity().getContent();

                    BufferedReader buffer = new BufferedReader(new InputStreamReader(content));
                    String s = "";
                    while ((s = buffer.readLine()) != null) {
                        response += s;
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return response;
        }
        protected void onPostExecute(String result) {
            if(result.equals("SomethingAwesomeForTheAppToRecognize")){
                authd = "true";
                show();
                finish();
            }
            else{
                textView.setText(result);
            }
        }
    }

    public void onClick(View view) {
        if(authd.equals("false")){
            LogInTask task = new LogInTask();
            task.execute(new String[] { a + a2 + a3 + a4 });
        }
        else
            show();
    }

    public void show()
    {
        final Context context = this;
        Intent intent = new Intent(context, Control.class);
        intent.putExtra("uid", a + a2 + a3 + a4 );
        startActivity(intent);
    }
}

Control.java Code:


package com.airlim.garageatrois;

import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.Settings;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.ToggleButton;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;


public class Control extends Activity {
    TextView textView;
    volatile String reverse = "false";
    volatile String uid = "0000";



    @Override
    public void onCreate(Bundle savedInstanceState) {
        Intent intent = getIntent();
        uid = intent.getStringExtra("uid");

        super.onCreate(savedInstanceState);
        setContentView(R.layout.control);
        ToggleButton b3 = (ToggleButton) findViewById(R.id.button3);
        ToggleButton b2 = (ToggleButton) findViewById(R.id.button2);
        ToggleButton b4 = (ToggleButton) findViewById(R.id.button4);
        textView = (TextView) findViewById(R.id.textView02);

        b3.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v) {
                textView.setText("Toggling light...");
                ToggleButton tb = (ToggleButton) v;
                tb.setEnabled(!tb.isEnabled());
                tb.setChecked(!tb.isChecked());
                ControlTask task = new ControlTask();
                task.execute(new String[] {"g_light"});

            }
        });
        b2.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v) {
                textView.setText("Toggling door...");
                ToggleButton tb = (ToggleButton) v;
                tb.setEnabled(!tb.isEnabled());
                tb.setChecked(!tb.isChecked());
                ControlTask task = new ControlTask();
                task.execute(new String[]{"g_door"});
            }
        });
        b4.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v) {
                textView.setText("Toggling lock...");
                ToggleButton tb = (ToggleButton) v;
                tb.setEnabled(!tb.isEnabled());
                tb.setChecked(!tb.isChecked());
                ControlTask task = new ControlTask();
                task.execute(new String[]{"g_lock"});
            }

        });


    }

    public void onBackPressed() {
        finish();
    }

    public class LoadData extends AsyncTask<Void, Void, Void> {
        ToggleButton b2 = (ToggleButton) findViewById(R.id.button2);
        TextView textView = (TextView) findViewById(R.id.textView02);
        ProgressBar myProgress = (ProgressBar) findViewById(R.id.progressBar);
        @Override
        protected void onPreExecute()
        {
            textView.setText("Toggling door...");
            myProgress.setMax(100);
            myProgress.setVisibility(View.VISIBLE);
        }
        protected Void doInBackground(Void... params)
        {
            int var = (reverse.equals("false")) ? 0 : 100;
            int goal = (reverse.equals("false")) ? 100 : 0;
            while(var != goal)
            {
                try {
                    Thread.sleep(114);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (reverse.equals("false"))
                    var++;
                else
                    var--;
                myProgress.setProgress(var);
                runOnUiThread(new Runnable() {

                    @Override
                    public void run() {
                    textView.setText("Moving...");
                    }
                });
            }

            return null;
        }
        @Override
        protected void onPostExecute(Void result)
        {
            b2.setEnabled(!b2.isEnabled());
            b2.setChecked(!b2.isChecked());
            textView.setText("Door toggled");
            if(reverse.equals("false"))
                reverse = "true";
            else
                reverse = "false";
            super.onPostExecute(result);
        }
    }

    private class ControlTask extends AsyncTask<String, String, String> {
        volatile String android_id = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
        protected String doInBackground(String... urls) {
            String response = "";
	    String server = "same server that Login.java has"
            for (String url : urls) {
                try {
                    HttpClient client = new DefaultHttpClient();
                    HttpPost httpPOST = new HttpPost(server);
                    List<NameValuePair> params = new ArrayList<NameValuePair>();
                    params.add(new BasicNameValuePair("UID", uid));
                    params.add(new BasicNameValuePair("DID", android_id));
                    params.add(new BasicNameValuePair("switch", url));
                    UrlEncodedFormEntity ent = new UrlEncodedFormEntity(params, HTTP.UTF_8);
                    httpPOST.setEntity(ent);
                    HttpResponse execute = client.execute(httpPOST);
                    InputStream content = execute.getEntity().getContent();
                    BufferedReader buffer = new BufferedReader(new InputStreamReader(content));
                    String s = "";
                    while ((s = buffer.readLine()) != null) {
                        response += s;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return response;
        }
        protected void onPostExecute(String result) {
            if(result.equals("Door toggled")){
                LoadData task = new LoadData();
                task.execute();
            }
            if(result.equals("Light toggled")){
                ToggleButton b3 = (ToggleButton) findViewById(R.id.button3);
                b3.setEnabled(!b3.isEnabled());
                b3.setChecked(!b3.isChecked());
                textView.setText(result);
            }
            if(result.equals("Lock toggled")){
                ToggleButton b4 = (ToggleButton) findViewById(R.id.button4);
                b4.setEnabled(!b4.isEnabled());
                b4.setChecked(!b4.isChecked());
                textView.setText(result);
            }
            if(result.equals("Log in")){
                finish();
            }
            else{
                textView.setText(result);
            }
        }

    }
}

AndroidManifest.xml Code:


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.airlim.garageatrois"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="15"
        android:targetSdkVersion="16" />
    <uses-permission android:name="android.permission.INTERNET"/>
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.airlim.garageatrois.Login"
            android:screenOrientation="portrait"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:screenOrientation="portrait"
            android:label="@string/app_name"
            android:name=".Control" >
        </activity>
    </application>
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>