Tuesday, October 18, 2011

Android Garage Door App

Ever wanted to check your garage door from you Android phone?
Now You can Open and Close Your Garage Door with Your Phone.


This "How To" guide is a complete tutorial, taking you through the entire process step by step. I have included a working Android app you can use "as-is" or use it as a starting point and improve it. At the end of the tutorial, I will document some things I would recommend be added to the app.

Let's get started. Open eclipse and start a new project. If you need help setting up your development environment, I have created a detailed step by step guide. Here is a link.

How to Setup Android Development Environment

Create a new project
Project name: Garage Door App
Build target: I targeted Android 2.3.1 but I believe the code would run as low as 1.6. So if you phone is not lower than v2.3.1 select the appropriate API
Application name: Garage Door App
Package name: com.acm.blog.example.garagedoorapp
Check "Create activity:" and name it "MainActivity"
Min SDK version: 9 (again if you target a lower API adjust this number to the appropriate API level)


Here is what our app is going to look like when done.

It is made up of three controls.

  1. An open garage door button.
  2. A close garage door button.
  3. And finally a status display which will let us know when the door is open or closed. I also coded it so when you touch the display graphic the app will check the door and update the status.
Let's start coding!
Step 1: Create the layout by editing the main.xml file already created for us. If you new to Android the layout might be a little confusing. Here is a complete explanation of the layout file.

  • Starting on line 1 and ending on line 19 is a "Linear Layout" that encompasses the entire layout. We specify that the orientation is vertical. As new controls get added they appear at the bottom. We also set the background to our brushed aluminum graphic.
  • Lines 2-4 are a Linear layout with a TextView for "Garage Door Opener". I wrapped the TextView in a linear layout so I could center it with android:gravity="center_horizontal"
  • I used a frame layout on lines 5-12 so that I can layer controls on top of each other. I wanted the progress bar to be dead center of our status graphic.
  • Lines 6-8 is our status graphic that is centered with the property android:gravity="center_horizontal"
  • Lines 9-11 is the progress bar that we display while making our web service calls in a background thread. Also centered.
  • Lines 13-18 is a linear layout (outside of the frame so it appears below it). This linear layout is set to stack the controls horizontally with the following property  android:orientation="horizontal". We have two image buttons in this layout.






Main.xml

  
   
  
  
   
    
   
   
    
   
  
  
    
     
    
     
  


 


Step 2: Before we code our main activity let's create a helper class to make the http calls to the Netduino.
  • Right click on the project name and select New > Class
  • Name it "WebService.java". Now lets go through the code.
  • Line 2 we will make a constant string containing the Netduino IP address
public class WebService {
 private final String WEBSERVICE_URL = "http://192.168.0.153/";
  ...
  ...
  • Line 3 declare an HttpClient we will use to make the HTTP cals
public class WebService {
 private final String WEBSERVICE_URL = "http://192.168.0.153/";
 private HttpClient client;
 ...
  • Create a constructor and in it instantiate our HttpClient
...
    public WebService(){
        client = new DefaultHttpClient();
    }
  ...
  • Create a private method that calls the webservice and parses the retun
private String MakeHTTPCall(String method){
  HttpGet get = new HttpGet(WEBSERVICE_URL + method);
  try {
   // Create a response handler
            ResponseHandler responseHandler = new BasicResponseHandler();
            String responseBody = client.execute(get, responseHandler);
            return responseBody;
  } catch (IOException e) {       // -|
   e.printStackTrace();       // |--> Oops something went wrong!
  } catch (Exception e) {        // |
   e.printStackTrace();       // -|
  }
  return "failed";         //Return "failed" if we encounter an error
 }
  • Create a new HTTP get passing in the web service URL and the method name
private String MakeHTTPCall(String method){
  HttpGet get = new HttpGet(WEBSERVICE_URL + method);
  try {
   // Create a response handler
            ResponseHandler responseHandler = new BasicResponseHandler();
            String responseBody = client.execute(get, responseHandler);
            return responseBody;
  } catch (IOException e) {       // -|
   e.printStackTrace();       // |--> Oops something went wrong!
  } catch (Exception e) {        // |
   e.printStackTrace();       // -|
  }
  return "failed";         //Return "failed" if we encounter an error
 }
  • Instantiate a new BasicResponseHandler to parse our webservice return into a string
  • Create a String variable to hold the return and call execute on the HttpClient
  • We pass in the HttpGet variable and the response handler
private String MakeHTTPCall(String method){
  HttpGet get = new HttpGet(WEBSERVICE_URL + method);
  try {
   // Create a response handler
            ResponseHandler responseHandler = new BasicResponseHandler();
            String responseBody = client.execute(get, responseHandler);
            return responseBody;
  } catch (IOException e) {       // -|
   e.printStackTrace();       // |--> Oops something went wrong!
  } catch (Exception e) {        // |
   e.printStackTrace();       // -|
  }
  return "failed";         //Return "failed" if we encounter an error
 }
  • Return the result from the HTTP GET call. We wrap the HTTP call in try catch.
private String MakeHTTPCall(String method){
  HttpGet get = new HttpGet(WEBSERVICE_URL + method);
  try {
   // Create a response handler
            ResponseHandler responseHandler = new BasicResponseHandler();
            String responseBody = client.execute(get, responseHandler);
            return responseBody;
  } catch (IOException e) {       // -|
   e.printStackTrace();       // |--> Oops something went wrong!
  } catch (Exception e) {        // |
   e.printStackTrace();       // -|
  }
  return "failed";         //Return "failed" if we encounter an error
 }






WebService.java
public class WebService {
 private final String WEBSERVICE_URL = "http://192.168.0.153/";
 private HttpClient client;
    public WebService(){
        client = new DefaultHttpClient();
    }

    public String IsGarageDoorOpen(){
     for(int i = 0; i < 5; i++){
         String s = MakeHTTPCall("checkdoor");
      if(s.equals("failed") == false)
       return s; //Got a response return result
     }
     return "failed";  //Oh well we tried 5 times without success
 }
 public String ActivateGarageDoor(){
  for(int i=0; i < 5; i++){
   String s = MakeHTTPCall("activatedoor");
      if(s.equals("failed") == false)
       return s; //Got a response return result
  }
  return "failed";
 }
 
 private String MakeHTTPCall(String method){
  HttpGet get = new HttpGet(WEBSERVICE_URL + method);
  try {
   // Create a response handler
            ResponseHandler responseHandler = new BasicResponseHandler();
            String responseBody = client.execute(get, responseHandler);
            return responseBody;
  } catch (IOException e) {       // -|
   e.printStackTrace();       // |--> Oops something went wrong!
  } catch (Exception e) {        // |
   e.printStackTrace();       // -|
  }
  return "failed";         //Return "failed" if we encounter an error
 }

}




Step 3: Let's edit our Main Activity.
  • Line 2 is our progress bar we will display while we check the status of the garage door in a background thread. It is always a good idea to let the user know when the app is working in the background.
  • Line 3 is an instance of our ImageView we will use to display the status of the garage door.
  • Line 4 is an instance of our web service helper class. 
public class MainActivity extends Activity {
 private ProgressBar progressBar1;
 private ImageView status;
 private WebService ws;
Now let's add some code to the onCreate method

  • On line 9 we turn of the window title bar.
  • Line 12 instantiate the progress bar.
  • Line 13 the ImageView we are using to display the status of the garage door.
  • Lines 14-19 set the on click listener so we can check the status of the garage door when someone touches the status image. We someone clicks the ImageView we execute an AsyncTask to check the garage door in a background thread.
  • Line 20 instantiate our Open ImageButton.
  • Lines 21-26 set the on click listener for the open button. When the open button is clicked we activate the garage door with an AsyncTask on a background thread.
  • Lines 27-33 is the same for the Close button.
  • Lines 34-48 We create a Timer to check the status of the garage door every fifteen minutes again on a background thread.
@Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);
  setContentView(R.layout.main);
  ws = new WebService();
  progressBar1 = (ProgressBar) findViewById(R.id.progressBar1);
  status = (ImageView) findViewById(R.id.Status_ImageView);
  status.setOnClickListener(new OnClickListener() {      
     public void onClick(View v) {      // Check garage door
      new CheckGarageDoorAsync().execute();   // when status
                 // image clicked
     } 
    }); 
  ImageButton openButton = (ImageButton) findViewById(R.id.OpenButton);
  openButton.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View arg0) {
    new ActivateGarageDoorAsync().execute();
   }
  });
  ImageButton closeButton = (ImageButton) findViewById(R.id.CloseButton);
  closeButton.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View arg0) {
    new ActivateGarageDoorAsync().execute();
   }
  });
  final Handler handler = new Handler();
  Timer t = new Timer();
  t.scheduleAtFixedRate(new TimerTask() {
   @Override
   public void run() {
    handler.post(new Runnable() {
     public void run() {
      Log.d("z", "check garage door");
      setUI(ws.IsGarageDoorOpen());
     }
    });
   }
  }, 0, 900000);
  new CheckGarageDoorAsync().execute();
 }
}

Lines 56-92 is a method that sets our UI depending on the state of the garage door. If the garage door is open we disable the open button and set the display to "Open" and of course the opposite if its closed.

private void setUI(String mode) {
  if (mode.equals("Open")) {
   ImageButton o = (ImageButton) findViewById(R.id.OpenButton);
   o.setImageResource(R.drawable.open_button_alt); // Open button
               // depressed
   o.setEnabled(false);        // Disable open button
   ImageButton c = (ImageButton) findViewById(R.id.CloseButton);
   c.setImageResource(R.drawable.close_button);  // Closed button up
   c.setEnabled(true);        // Enable close button
   ImageView status = (ImageView) findViewById(R.id.Status_ImageView);
   status.setImageResource(R.drawable.status_open); // Set status
                // graphic
  } else if (mode.equals("Closed")) {
   ImageButton o = (ImageButton) findViewById(R.id.OpenButton);
   o.setImageResource(R.drawable.open_button);  // Open button up
   o.setEnabled(true);        // Enable open button
   ImageButton c = (ImageButton) findViewById(R.id.CloseButton);
   c.setImageResource(R.drawable.close_button_alt); // Close button
                // depressed
   c.setEnabled(false);         // Disable close button
   ImageView status = (ImageView) findViewById(R.id.Status_ImageView);
   status.setImageResource(R.drawable.status_closed);// Set status
                 // graphic
  } else {
   ImageButton o = (ImageButton) findViewById(R.id.OpenButton);
   o.setImageResource(R.drawable.open_button_alt);  // Open button
                // depressed
   o.setEnabled(false);         // Disable open button
   ImageButton c = (ImageButton) findViewById(R.id.CloseButton);
   c.setImageResource(R.drawable.close_button_alt); // Close button
                // depressed
   c.setEnabled(false);         // Disable close button
   ImageView status = (ImageView) findViewById(R.id.Status_ImageView);
   status.setImageResource(R.drawable.status_unknown);// Set status
                  // graphic
  }
 }

Lines 97-117 We create an AsyncTask to check the status of the garage door. Using an AsyncTask allows us the make an http call on a background thread so our app does not become un-responsive and geneate an "ANR" error. If you perform a long task on the UI thread and the user presses a key and there is no response within 5 seconds Android will generate an ANR error message asking the user to wait or to close. You should never perform a network call on the UI thread. We implement three methods of the AsyncTask.

  1. onPreExecute(). This is performed on the UI thread. We turn on the progress bar indicating we are checking the door.
  2. doInBackground(). This is performed in a background thread. In this method we check the garage door state by calling our IsGarageOpen method on our web service class. We then pass the results to onPostExecute() method.
  3. onPostExecute(). This is performed on the UI thread. This method sets all the controls based on the state of the garage door.

private class CheckGarageDoorAsync extends AsyncTask {
  @Override
  protected void onPreExecute() {
   setUI("Unkown");            // While we check disable all buttons and clear status display
   progressBar1.setVisibility(View.VISIBLE); // Display progress bar
  }
 
  @Override
  protected String[] doInBackground(Void... arg0) {
   String r = ws.IsGarageDoorOpen();    // Make webservice call to check
              // garage door
   return new String[] { r };      // Return result
  }
 
  @Override
  protected void onPostExecute(String[] result) {
   progressBar1.setVisibility(View.INVISIBLE); // Hide progress bar
   setUI(result[0]);        // Pass result to setUI method to sync the UI
  }
 
 }

Lines 122-161 We create another AsyncTask to activate the garage door. Here is the complete MainActivity class.
MainActivity.java
public class MainActivity extends Activity {
 private ProgressBar progressBar1;
 private ImageView status;
 private WebService ws;

 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);
  setContentView(R.layout.main);
  ws = new WebService();
  progressBar1 = (ProgressBar) findViewById(R.id.progressBar1);
  status = (ImageView) findViewById(R.id.Status_ImageView);
  status.setOnClickListener(new OnClickListener() {      
     public void onClick(View v) {      // Check garage door
      new CheckGarageDoorAsync().execute();   // when status
                 // image clicked
     } 
    }); 
  ImageButton openButton = (ImageButton) findViewById(R.id.OpenButton);
  openButton.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View arg0) {
    new ActivateGarageDoorAsync().execute();
   }
  });
  ImageButton closeButton = (ImageButton) findViewById(R.id.CloseButton);
  closeButton.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View arg0) {
    new ActivateGarageDoorAsync().execute();
   }
  });
  final Handler handler = new Handler();
  Timer t = new Timer();
  t.scheduleAtFixedRate(new TimerTask() {
   @Override
   public void run() {
    handler.post(new Runnable() {
     public void run() {
      Log.d("z", "check garage door");
      setUI(ws.IsGarageDoorOpen());
     }
    });
   }
  }, 0, 900000);
  new CheckGarageDoorAsync().execute();
 }

 /*
  * Set UI components Garage Open Open button is disabled Close button is
  * enabled Status graphic - OPEN Garage Closed Open button is enabled Close
  * button is disabled Status graphic - Closed Garage Unknown Open button is
  * disabled Close button is disabled Status graphic - black
  */
 private void setUI(String mode) {
  if (mode.equals("Open")) {
   ImageButton o = (ImageButton) findViewById(R.id.OpenButton);
   o.setImageResource(R.drawable.open_button_alt); // Open button
               // depressed
   o.setEnabled(false);        // Disable open button
   ImageButton c = (ImageButton) findViewById(R.id.CloseButton);
   c.setImageResource(R.drawable.close_button);  // Closed button up
   c.setEnabled(true);        // Enable close button
   ImageView status = (ImageView) findViewById(R.id.Status_ImageView);
   status.setImageResource(R.drawable.status_open); // Set status
                // graphic
  } else if (mode.equals("Closed")) {
   ImageButton o = (ImageButton) findViewById(R.id.OpenButton);
   o.setImageResource(R.drawable.open_button);  // Open button up
   o.setEnabled(true);        // Enable open button
   ImageButton c = (ImageButton) findViewById(R.id.CloseButton);
   c.setImageResource(R.drawable.close_button_alt); // Close button
                // depressed
   c.setEnabled(false);         // Disable close button
   ImageView status = (ImageView) findViewById(R.id.Status_ImageView);
   status.setImageResource(R.drawable.status_closed);// Set status
                 // graphic
  } else {
   ImageButton o = (ImageButton) findViewById(R.id.OpenButton);
   o.setImageResource(R.drawable.open_button_alt);  // Open button
                // depressed
   o.setEnabled(false);         // Disable open button
   ImageButton c = (ImageButton) findViewById(R.id.CloseButton);
   c.setImageResource(R.drawable.close_button_alt); // Close button
                // depressed
   c.setEnabled(false);         // Disable close button
   ImageView status = (ImageView) findViewById(R.id.Status_ImageView);
   status.setImageResource(R.drawable.status_unknown);// Set status
                  // graphic
  }
 }

 /*
  * Check the garage door on background thread with an AsyncTask
  */
 private class CheckGarageDoorAsync extends AsyncTask {
  @Override
  protected void onPreExecute() {
   setUI("Unkown");            // While we check disable all buttons and clear status display
   progressBar1.setVisibility(View.VISIBLE); // Display progress bar
  }

  @Override
  protected String[] doInBackground(Void... arg0) {
   String r = ws.IsGarageDoorOpen();    // Make webservice call to check
              // garage door
   return new String[] { r };      // Return result
  }

  @Override
  protected void onPostExecute(String[] result) {
   progressBar1.setVisibility(View.INVISIBLE); // Hide progress bar
   setUI(result[0]);        // Pass result to setUI method to sync the UI
  }

 }

 /*
  * Activate the garage door on a background thread with an AsyncTask
  */
 private class ActivateGarageDoorAsync extends
   AsyncTask {
  @Override
  protected void onPreExecute() {
   setUI("Unkown");        // While we check disable all buttons and clear
              // display graphic
   progressBar1.setVisibility(View.VISIBLE); // Display progress bar
  } 

  @Override
  protected String[] doInBackground(String... arg0) {
   String start = ws.IsGarageDoorOpen();

   String r = ws.ActivateGarageDoor();   // Activate garage door
   for (int i = 0; i < 60; i++) {     // Give the door up to 1 minute to
              // close/open
    try {
     Thread.sleep(1000);     // Pause for 1 second
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
    r = ws.IsGarageDoorOpen();     // Check the door status
    if (r.equals(start) == false && r.equals("failed") == false)
     break;         // The door is done opening/closing
   }
   return new String[] { r };      // Return the status of garage
  }

  @Override
  protected void onProgressUpdate(String... values) {

  }

  @Override
  protected void onPostExecute(String[] result) {
   progressBar1.setVisibility(View.INVISIBLE);
   setUI(result[0]);
  }

 }
}

You can get the entire project source code here


To learn how to build the hardware required to monitor and control your garage door read my post here. http://androidcodemonkey.blogspot.com/2011/09/mobile-app-to-monitor-open-and-close.html. If you have any questions please feel free to leave a comment. I usually answer questions within a day or two.

43 comments:

JC said...

excellent tutorials. Thank you v much :)

Anonymous said...

Thank you so much for taking the time to post these tutorials. I have built all the hardware and software and the end result is amazing. I can now check the status of my garage and let people in from anywhere in the world.

I have added the following extras:
1. A second garage control
2. Email. I get an email anytime someone opens or closes the garage. I am sure I will turn this off as soon as I get comfortable with how it works. It also sends me an email at 10 and 11pm if the garage is still open. I have chosen not to automatically shut it but am considering it.
3. Settings in android app. I am in the process of added a menu item to be able to change the ip and recipient email address(es).

Anyway, I really appreciate the great work you have done here.

Unknown said...

Awesome! Great to hear you built everything and made it better! Let me know if you add on any additional features.

Anonymous said...

Hi Greg,
Can you elaborate on the Restful Web Service. After examining the code, I don't see where there is any access outside your private home network. How do the Android App and the Netuino programs talk to each other over the internet?

Dennis

Anonymous said...

Hi , I am not able to download the code

Unknown said...

Might have been a momentary issue with SkyDrive. I just tested it and it seems to work fine. Can you try it again and let me know if your still having trouble.

Kyle Bale said...

Man, wish I could get the code from the guy that adapted it to 2 garage doors.

Anonymous said...

Hello Greg.

I'm doing your tutorial but i get stuck in the MainActivity.java starting at line 10 'setContentView(R.layout.main);' I get the message "R cannot be resolved to a variable" (I get the same message on other lines where R is being used. Eclipse shows me 10 quickfixes :-)
I have tried some but the message staid.

Could you advise me in this?

Kind regards,
Seb.

Anonymous said...

Sorry, forget my last question, I had created a new class but see now that there was already a MainActivity.java :-)

vignesh said...

how we generate webservice url can we write any paticular api for webservice method please explain that clearly im stuck in webservice

vignesh said...

Hi Greg,
Can you elaborate on the Restful Web Service. After examining the code, I don't see where there is any access outside your private home network. How do the Android App and the Netuino programs talk to each other over the internet?

indolent said...

I'm interested in the app the anonymous reference above where he added a setting to be able to specify the ip address in the app itself. Has anyone modified the app like that and have it available for download somewhere?

I'm assuming this app could accept a dynamic dns type service in place of just using a local ip address?

Unknown said...

tyleragent,
Will that "old fashioned" opener allow you to open or close the garage from anywhere in the world? ;)

Leona said...

I am for sure returning again for more contents of yours.
IPhone 4S apps

Replacement Garage Door Phoenix said...

This application is perfect to control garage door using smart phone.

Anonymous said...

Whether somebody pursuit of his vital thing, hence he or she desires to be accessible that at length, hence that thing is maintained over here.Rod iron doors

Anonymous said...

Thankfulness to my dad who informed me relating to this blog, this website is really amazing. lowermycellphoneplan.com/solavei/

anjila smith said...

A gigantic moonlike of recommendation, keep moving on. Garagedoormn

jacob said...

this is an amazing, you have listed entire code for checking if your garage door is open or closed.

Anna Schafer said...

Let's get started. Open eclipse and start a new project. If you need help setting up your development environment, I have created a detailed step by step guide. Here is a link.garage door torrance ca

Unknown said...

All Garage Doors Repair service and repair all types of garage doors including commercial and residential. If you need garage door repair or installation in the Southern California area, Our pro's can help.

brittanymlemay said...

Let's get started. Open eclipse and start a new project. If you need help setting up your development environment, I have created a detailed step by step guide. Here is a link. garage door repair

Automatic Driveway Gate San Diego said...

I like the Valuable Information you provide in your articles. I'll bookmark your weblog and check again here frequently. I am quite sure I will learn many new stuff right here!

Budget Garage Door San Diego
Contemporary Garage Door San Diego

Anonymous said...

Here www.garagedoorrepairsanfranciscoca.com , you may choose varieties of style you want and at very accessible and affordable budget. Come visit and prove it to yourself!

Unknown said...

Great info. I love all the posts, I really enjoyed, I would like more information about this, because it is very nice., Thanks for sharing.
happy wheels , agario , happywheels , agar ,

Unknown said...

Very helpful advice in this particular post! It’s the little changes that make the largest changes. Thanks for sharing!
fireboy watergirl | ssf2 , fireboy and watergirl games | super smash flash

Unknown said...

Nice to read your article! I am looking forward to sharing your adventures and experiences.
garage door service

Unknown said...

This is such a great resource that you are providing and you give it away for free. I love seeing blog that understand the value of providing a quality resource for free.
garage door repairs charlotte

Unknown said...

Wow!..Amazing.. I have no words to describe it

fireboy watergirl
Geometry Dash

spirulina plus tablet said...

I am really empowered with your writing talent. Anyway keep up the wonderful high
quality writing, it’s rare to see a great blog
such as this these days

Unknown said...

Ahaa, its pleasant discussion on the topic of this article at this place at this web site, I have read all that, so now me also commenting here.
penyebab anak usia 2 tahun belum bisa berjalan
cara menghilangkan bintik bintik kecil di wajah seperti jerawat
bagaimana cara merawat luka diabetes
cara mengobati varises vulva
Obat Cepat Gemuk Dalam Waktu Singkat

Linda said...

Thank you for sharing valuable information. Nice post. I enjoyed reading this post.
Papa Louie 2

Run 3 | Stick Run 2 |friv4school | girls go games
unblocked games

Unknown said...

Can you elaborate on the Restful Web Service. After examining the code, I don't see where there is any access outside your private home network. How do the Android App and the Netuino programs talk to each other over the internet?
hotmail login

Jack son said...

This means now it is perilous to keep your carport entryway open. Entering and leaving through the entryway is currently dangerous as well. You don't need a massive way to pummel down on you. Garage Door Installation Perris

Unknown said...

Being new to the blogging world I feel like there is still so much to learn. Your tips helped to clarify a few things for me as well as giving..
iOS Training in Chennai
Android Training in Chennai
php Training in Chennai

Unknown said...

Garage doors are very easy to operate with just a mobile application and are best way for home and family security. Along with the benefit of ease of operation these garage doors increase property value.
Garage Door Repair North Park

Kavi said...

Nice it seems to be good post... It will get readers engagement on the article since readers engagement plays an vital role in every

blog.. i am expecting more updated posts from your hands.
Mobile App Development Company
Mobile App Development Company in India
Mobile App Development Companies

alicetaylor said...

Thanks for all your information, Website is very nice and informative content

dorian approval

Unknown said...

Keep on updating these kinds of informative things... friv4schoolonline.net

lockoutgaragedoorssd said...

Great!!Thanks for posting such a nice and informative blog.I appreciate you for posting this blog.

garage door services san diego ca

Masud Rana said...
This comment has been removed by the author.
Masud Rana said...

Android garage door opener app, lets you easily open and close your Chamberlain GDO WiFi products from your cell phone or mobile

link text

Adiba Alam said...

Having experienced comparable maturing and weathering, one broken carport entryway spring is an indication that the second one will soon break as well. Garage Doors Westlake Village

Post a Comment

Note: Only a member of this blog may post a comment.