How to make sure tiles show the proper state for garage door control

Hi,
I’m working on a simple device handler to work with the Arduino ThingShield and things are going very well for the most part. But I’m now at the point where I need to initialize the three door state variables to the values of the three door tilt sensors (garage doors). Here is my code. I looked all around the documentation but the best I could find was setting “defaultState” to true for the state you want to be the default. But I don’t want a hard coded default. I want the app to be able just reflect the tilt sensor statuses.

http://docs.smartthings.com/en/latest/device-type-developers-guide/tiles-metadata.html#state-selection-algorithm

/**
 *  Copyright 2016 Justin Eltoft
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 *
 */
metadata {
	definition (name: "Garage Door Shield", namespace: "jdeltoft", author: "Justin Eltoft") {
		capability "Actuator"
		capability "Switch"
		capability "Sensor"
        
        command "door_one_open"
        command "door_one_close"
        command "door_two_open"
        command "door_two_close"
        command "door_three_open"
        command "door_three_close"
	}

	// Simulator metadata
	simulator {
		status "on":  "catchall: 0104 0000 01 01 0040 00 0A21 00 00 0000 0A 00 0A6F6E"
		status "off": "catchall: 0104 0000 01 01 0040 00 0A21 00 00 0000 0A 00 0A6F6666"

		// reply messages
		reply "raw 0x0 { 00 00 0a 0a 6f 6e }": "catchall: 0104 0000 01 01 0040 00 0A21 00 00 0000 0A 00 0A6F6E"
		reply "raw 0x0 { 00 00 0a 0a 6f 66 66 }": "catchall: 0104 0000 01 01 0040 00 0A21 00 00 0000 0A 00 0A6F6666"
	}

	// UI tile definitions
	tiles(scale: 2) {
		standardTile("door_one", "device.switch", width: 2, height: 2, canChangeIcon: true, canChangeBackground: true) {
			state "door_one_open", label: 'Open', action: "door_one_close", icon: "st.Transportation.transportation13", backgroundColor: "#79b821"
			state "door_one_close", label: 'Closed', action: "door_one_open", icon: "st.Transportation.transportation14", backgroundColor: "#ffffff"
		}
        
		standardTile("door_two", "device.switch", width: 2, height: 2, canChangeIcon: true, canChangeBackground: true) {
			state "door_two_open", label: 'Open', action: "door_two_close", icon: "st.Transportation.transportation13", backgroundColor: "#79b821"
			state "door_two_close", label: 'Closed', action: "door_two_open", icon: "st.Transportation.transportation14", backgroundColor: "#ffffff", defaultState: true
		}
        
		standardTile("door_three", "device.switch", width: 2, height: 2, canChangeIcon: true, canChangeBackground: true) {
			state "door_three_open", label: 'Open', action: "door_three_close", icon: "st.Transportation.transportation13", backgroundColor: "#79b821"
			state "door_three_close", label: 'Closed', action: "door_three_open", icon: "st.Transportation.transportation14", backgroundColor: "#ffffff"
		}
        
		//standardTile("switch", "device.switch", width: 1, height: 1, canChangeIcon: true, canChangeBackground: true) {
			//state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821"
			//state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
		//}

		main ('door_one')
		details (['door_one', 'door_two', 'door_three'])
	}
}

def installed() {
	log.trace "installed function was called"
    initialize()
}
def updated() {
	log.trace "updated function was called"
    initialize()
}

def initialize() {
	//// need to read out three door states here and then send init conditions to arduino
	log.trace "initialize function was called"
	//zigbee.smartShield(text: "door_one_init_close").format()
    //sendEvent(name: "door_one", value: "door_one_close")
	//zigbee.smartShield(text: "door_two_init_close").format()
    //sendEvent(name: "door_two", value: "door_two_close")
	//zigbee.smartShield(text: "door_three_init_close").format()
    //sendEvent(name: "door_three", value: "door_three_close")
}


// Parse incoming device messages to generate events
def parse(String description) {
	def value = zigbee.parse(description)?.text
	def name
	//log.trace "testing $value"
	if (value in ["door_one_open","door_one_close"]) {
       name = "door_one"
    } else if (value in ["door_two_open","door_two_close"]) {
       name = "door_two"
    } else if (value in ["door_three_open","door_three_close"]) {
       name = "door_three"
    } else {
       name = null
    }
	def result = createEvent(name: name, value: value)
	log.debug "Parse returned ${result?.descriptionText}"
	return result
}

//def parse(String description) {
//	def value = zigbee.parse(description)?.text
//	def name = value in ["on","off"] ? "switch" : null
//	def result = createEvent(name: name, value: value)
//	log.debug "Parse returned ${result?.descriptionText}"
//	return result
//}

// Commands sent to the device
//def on() {
	//zigbee.smartShield(text: "on").format()
//}

//def off() {
	//zigbee.smartShield(text: "off").format()
//}

def door_one_open() {
	zigbee.smartShield(text: "door_one_open").format()
}
def door_one_close() {
	zigbee.smartShield(text: "door_one_close").format()
}

def door_two_open() {
	zigbee.smartShield(text: "door_two_open").format()
}
def door_two_close() {
	zigbee.smartShield(text: "door_two_close").format()
}

def door_three_open() {
	zigbee.smartShield(text: "door_three_open").format()
}
def door_three_close() {
	zigbee.smartShield(text: "door_three_close").format()
}

Ok, I’m guessing at this point it has to do with not using a standard capability? So if I use “Switch” capability with “on” and “off” states, then the state seems to be remembered when navigating around in the phone app. But here I’m using custom states. I tried adding this:

    attribute "door_one", "string"
    attribute "door_two", "string"
    attribute "door_three", "string"

and now I do see when I go to this shield in my devices, that “Current State” is now kept from the SmartThings developer website. But still, the app won’t remember what state these tiles are in any time I move away from the detail or the thing view in the phone app.

Perhaps I can try using a standard “garage door” capability next.

http://docs.smartthings.com/en/latest/capabilities-reference.html#garage-door-control

Ok, found the issue. my second parameter to the tiles was device.switch but that needed to be the device.door_one etc. Here is my current code for both arduino and device handler. I still need to add three reed switch sensors on the physical doors. That way if a door gets interrupted (the light sensor or hitting something) and returns to the old state, I’ll always know the real state of the doors and the icons in the garage door opener will always be proper.

ARDUINO

//*****************************************************************************
/// @file
/// @brief
///   Arduino SmartThings Shield LED Example 
/// @note
///              ______________
///             |              |
///             |         SW[] |
///             |[]RST         |
///             |         AREF |--
///             |          GND |--
///             |           13 |-- X LED
///             |           12 |--
///             |           11 |--
///           --| 3.3V      10 |-- X_THING_RX FIX
///           --| 5V         9 |-- DOOR_THREE
///           --| GND        8 |-- DOOR_TWO
///           --| GND          |
///           --| Vin        7 |-- DOOR_ONE
///             |            6 |--
///           --| A0         5 |--
///           --| A1    ( )  4 |--
///           --| A2         3 |-- X THING_RX
///           --| A3  ____   2 |-- X THING_TX
///           --| A4 |    |  1 |--
///           --| A5 |    |  0 |--
///             |____|    |____|
///                  |____|
///
//*****************************************************************************
#include <SoftwareSerial.h>   //TODO need to set due to some weird wire language linker, should we absorb this whole library into smartthings
#include <SmartThings.h>

//*****************************************************************************
// Pin Definitions    | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
//                    V V V V V V V V V V V V V V V V V V V V V V V V V V V V V
//*****************************************************************************
#define PIN_THING_RX         10
#define PIN_THING_RX_RESERVE 3
#define PIN_THING_TX         2
#define PIN_DOOR_ONE         7
#define PIN_DOOR_TWO         8
#define PIN_DOOR_THREE       9

//*****************************************************************************
// Global Variables   | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
//                    V V V V V V V V V V V V V V V V V V V V V V V V V V V V V
//*****************************************************************************
SmartThingsCallout_t messageCallout;    // call out function forward decalaration
SmartThings smartthing(PIN_THING_RX, PIN_THING_TX, messageCallout);  // constructor

bool isDebugEnabled;    // enable or disable debug in this example
int door_one_state;
int door_two_state;
int door_three_state;

//*****************************************************************************
// Local Functions  | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
//                  V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V
//*****************************************************************************
void door_one(int cmd)
{
  int changed = 0;
  if (cmd == 1 && door_one_state == 0) {
     door_one_state = 1;
     changed = 1;
  }
  else if (cmd == 0 && door_one_state == 1) {
     door_one_state = 0;
     changed = 1;
  }

  if (changed == 1) {
     smartthing.shieldSetLED(0, 3, 1);
     digitalWrite(PIN_DOOR_ONE, HIGH);  // press button
     delay(780);
     digitalWrite(PIN_DOOR_ONE, LOW);   // unpress button
     smartthing.shieldSetLED(0, 0, 0);

     // Need to change this code to just read the true physical state of the gararge doors with a reed switch
     // setup a loop with a timeout looking for a changed state and if it doesn't change just send the current state
     // but should make sure we wait at least some time to prevent sending the open/closed state and having the door snag and return then to the old state
     delay(3000);
     if (cmd == 1) {
       smartthing.send("door_one_open");
     }
     else
     {
       smartthing.send("door_one_close");
     }
     delay(300);
     validateDoorStates();
   }
}

void door_two(int cmd)
{
  int changed = 0;
  if (cmd == 1 && door_two_state == 0) {
     door_two_state = 1;
     changed = 1;
  }
  else if (cmd == 0 && door_two_state == 1) {
     door_two_state = 0;
     changed = 1;
  }

  if (changed == 1) {
     smartthing.shieldSetLED(3, 0, 2);
     digitalWrite(PIN_DOOR_ONE, HIGH);  // press button
     delay(780);
     digitalWrite(PIN_DOOR_ONE, LOW);   // unpress button
     smartthing.shieldSetLED(0, 0, 0);

     // Need to change this code to just read the true physical state of the gararge doors with a reed switch
     // setup a loop with a timeout looking for a changed state and if it doesn't change just send the current state
     delay(3000);
     if (cmd == 1) {
       smartthing.send("door_two_open");
     }
     else
     {
       smartthing.send("door_two_close");
     }
     delay(300);
     validateDoorStates();
  }
}

void door_three(int cmd)
{
  int changed = 0;
  if (cmd == 1 && door_three_state == 0) {
     door_three_state = 1;
     changed = 1;
  }
  else if (cmd == 0 && door_three_state == 1) {
     door_three_state = 0;
     changed = 1;
  }

  if (changed == 1) {
     smartthing.shieldSetLED(5, 1, 5);
     digitalWrite(PIN_DOOR_ONE, HIGH);  // press button
     delay(780);
     digitalWrite(PIN_DOOR_ONE, LOW);   // unpress button
     smartthing.shieldSetLED(0, 0, 0);

     // Need to change this code to just read the true physical state of the gararge doors with a reed switch
     // setup a loop with a timeout looking for a changed state and if it doesn't change just send the current state
     delay(3000);
     if (cmd == 1) {
       smartthing.send("door_three_open");
     }
     else
     {
       smartthing.send("door_three_close");
     }
     delay(300);
     validateDoorStates();
  }
}

//*****************************************************************************
// API Functions    | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
//                  V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V
//*****************************************************************************
void setup()
{
  // setup default state of global variables
  isDebugEnabled = true;
  
  // setup hardware pins 
   smartthing.shieldSetLED(0, 0, 0);
   for (int i=PIN_DOOR_ONE; i <= PIN_DOOR_THREE; i++){
      pinMode(i, OUTPUT);
      digitalWrite(i, LOW);
   } 

   // Need to update this section to read out the true physical state of the doors with reed switches
   //door_one_state   = 1;
   //door_two_state   = 1;
   //door_three_state = 0;
   initializeDoorStates();
   
  if (isDebugEnabled)
  { // setup debug serial port
    Serial.begin(9600);         // setup serial with a baud rate of 9600
    Serial.println("setup..");  // print out 'setup..' on start
  }
}

//*****************************************************************************
void loop()
{
  // run smartthing logic 
  smartthing.run();

  // for every Nth time through this loop, validate the door states are valid per the physical switches
  validateDoorStates();
}

//*****************************************************************************
void messageCallout(String message)
{
  // if debug is enabled print out the received message
  if (isDebugEnabled)
  {
    Serial.print("Received message: '");
    Serial.print(message);
    Serial.println("' ");
  }

  if (message.equals("door_one_open"))
  {
    door_one(1);
  }
  else if (message.equals("door_one_close"))
  {
    door_one(0);
  }
  else if (message.equals("door_two_open"))
  {
    door_two(1);
  }
  else if (message.equals("door_two_close"))
  {
    door_two(0);
  }
  else if (message.equals("door_three_open"))
  {
    door_three(1);
  }
  else if (message.equals("door_three_close"))
  {
    door_three(0);
  }
}

void initializeDoorStates (void) {
  // read out reed switch for doors 1 thru 3
  // send smartThings that state and set the state here
  smartthing.send("door_one_open");
  door_one_state = 1;
  delay(500);
  smartthing.send("door_two_open");
  door_two_state = 1;
  delay(500);
  smartthing.send("door_three_close");
  door_three_state = 0;
  delay(500);
}

void validateDoorStates (void) {
  // read out reed switch for door 1 thru 3
  // set door_one_state = 1 or 0
  // send a one time initial message to smartThings with the latest door state and then only on changes
}

Thingshield device handler

/**
 *  Copyright 2016 Justin Eltoft
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 *
 */
metadata {
	definition (name: "Garage Door Shield", namespace: "jdeltoft", author: "Justin Eltoft") {
		capability "Actuator"
		//capability "Switch"
		capability "Sensor"
		capability "Garage Door Control"
		capability "Door Control"
        
        //attribute "door_one", "enum", ["door_one_open", "door_one_close"]
        //attribute "door_two", "enum", ["door_two_open", "door_two_close"]
        //attribute "door_three", "enum", ["door_three_open", "door_three_close"]
        attribute "door_one", "string"
        attribute "door_two", "string"
        attribute "door_three", "string"
        
        command "door_one_open"
        command "door_one_close"
        command "door_two_open"
        command "door_two_close"
        command "door_three_open"
        command "door_three_close"
	}

	// Simulator metadata
	simulator {
		status "on":  "catchall: 0104 0000 01 01 0040 00 0A21 00 00 0000 0A 00 0A6F6E"
		status "off": "catchall: 0104 0000 01 01 0040 00 0A21 00 00 0000 0A 00 0A6F6666"

		// reply messages
		reply "raw 0x0 { 00 00 0a 0a 6f 6e }": "catchall: 0104 0000 01 01 0040 00 0A21 00 00 0000 0A 00 0A6F6E"
		reply "raw 0x0 { 00 00 0a 0a 6f 66 66 }": "catchall: 0104 0000 01 01 0040 00 0A21 00 00 0000 0A 00 0A6F6666"
	}

	// UI tile definitions
    // Is there a way to prevent any tile press if one of the three tiles here is in the transition "next" state?  I know the given tile in that transition state is locked but the others aren't
	tiles(scale: 2) {
		standardTile("door_one", "device.door_one", width: 2, height: 2, canChangeIcon: true, canChangeBackground: true) {
			state "door_one_open", label: 'Open', action: "door_one_close", icon: "st.doors.garage.garage-open", backgroundColor: "#79b821", nextState:"closing"
			state "door_one_close", label: 'Closed', action: "door_one_open", icon: "st.doors.garage.garage-closed", backgroundColor: "#ffffff", nextState:"opening"
            state("opening", label:'Opening', icon:"st.doors.garage.garage-opening", backgroundColor:"#ffe71e")
			state("closing", label:'Closing', icon:"st.doors.garage.garage-closing", backgroundColor:"#ffe71e")
		}
        
		standardTile("door_two", "device.door_two", width: 2, height: 2, canChangeIcon: true, canChangeBackground: true) {
			state "door_two_open", label: 'Open', action: "door_two_close", icon: "st.doors.garage.garage-open", backgroundColor: "#79b821", nextState:"closing"
			state "door_two_close", label: 'Closed', action: "door_two_open", icon: "st.doors.garage.garage-closed", backgroundColor: "#ffffff", nextState:"opening"
            state("opening", label:'Opening', icon:"st.doors.garage.garage-opening", backgroundColor:"#ffe71e")
			state("closing", label:'Closing', icon:"st.doors.garage.garage-closing", backgroundColor:"#ffe71e")
		}
        
		standardTile("door_three", "device.door_three", width: 2, height: 2, canChangeIcon: true, canChangeBackground: true) {
			state "door_three_open", label: 'Open', action: "door_three_close", icon: "st.doors.garage.garage-open", backgroundColor: "#79b821", nextState:"closing"
			state "door_three_close", label: 'Closed', action: "door_three_open", icon: "st.doors.garage.garage-closed", backgroundColor: "#ffffff", nextState:"opening"
            state("opening", label:'Opening', icon:"st.doors.garage.garage-opening", backgroundColor:"#ffe71e")
			state("closing", label:'Closing', icon:"st.doors.garage.garage-closing", backgroundColor:"#ffe71e")

		}
        
		main ('door_one')
		details (['door_one', 'door_two', 'door_three'])
	}
}

def installed() {
	log.trace "installed function was called"
    //initialize()
}
def updated() {
	log.trace "updated function was called"
    //initialize()
}

def initialize() {
	log.trace "initialize function was called"
}

def refresh() {
	log.trace "refresh function was called"
}

// Parse incoming device messages to generate events
def parse(String description) {
    //log.debug "Parsing '${description}'"
    
	def value = zigbee.parse(description)?.text
	def name
	//log.trace "testing $value"
	if (value in ["door_one_open","door_one_close"]) {
       name = "door_one"
    } else if (value in ["door_two_open","door_two_close"]) {
       name = "door_two"
    } else if (value in ["door_three_open","door_three_close"]) {
       name = "door_three"
    } else {
       name = null
    }
	def result = createEvent(name: name, value: value)
	log.debug "Parse returned ${result?.descriptionText}"
	return result
}

def door_one_open() {
	zigbee.smartShield(text: "door_one_open").format()
}
def door_one_close() {
	zigbee.smartShield(text: "door_one_close").format()
}

def door_two_open() {
	zigbee.smartShield(text: "door_two_open").format()
}
def door_two_close() {
	zigbee.smartShield(text: "door_two_close").format()
}

def door_three_open() {
	zigbee.smartShield(text: "door_three_open").format()
}
def door_three_close() {
	zigbee.smartShield(text: "door_three_close").format()
}
1 Like

I should mention also that I’m using three of these reed switches (coto 9007-05-01) with the project. The little proto area of the thing shield was perfect for fitting this SIP packages:

Here is a photo

The white wire there is because of this issue with Arduino Mega and the thingShield

Ok, cleaned up a few things, setup the proper delays for doors to open (about 13 seconds so I gave it 14), and now I’m using A0,A1,A2 for reading out true door states. I’ll post it on GitHub but for now just wanted to share here. I also rearranged the tiles a bit for one main door and two side doors.

ARDUINO

//*****************************************************************************
/// @file
/// @brief
///   Arduino SmartThings Shield LED Example 
/// @note
///                         ______________
///                        |              |
///                        |         SW[] |
///                        |[]RST         |
///                        |         AREF |--
///                        |          GND |--
///                        |           13 |-- X LED
///                        |           12 |--
///                        |           11 |--
///                      --| 3.3V      10 |-- X_THING_RX FIX
///                      --| 5V         9 |-- DOOR_THREE
///                      --| GND        8 |-- DOOR_TWO
///                      --| GND          |
///                      --| Vin        7 |-- DOOR_ONE
///                        |            6 |--
///          Reed Door 1 --| A0         5 |--
///          Reed Door 2 --| A1    ( )  4 |--
///          Reed Door 3 --| A2         3 |-- X THING_RX
///                      --| A3  ____   2 |-- X THING_TX
///                      --| A4 |    |  1 |--
///                      --| A5 |    |  0 |--
///                        |____|    |____|
///                             |____|
///
//*****************************************************************************
#include <SoftwareSerial.h>   //TODO need to set due to some weird wire language linker, should we absorb this whole library into smartthings
#include <SmartThings.h>

//*****************************************************************************
// Pin Definitions    | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
//                    V V V V V V V V V V V V V V V V V V V V V V V V V V V V V
//*****************************************************************************
#define PIN_THING_RX         10
#define PIN_THING_RX_RESERVE 3
#define PIN_THING_TX         2
#define PIN_DOOR_ONE         7
#define PIN_DOOR_TWO         8
#define PIN_DOOR_THREE       9
#define PIN_DOOR_ONE_REED    A0
#define PIN_DOOR_TWO_REED    A1
#define PIN_DOOR_THREE_REED  A2

//*****************************************************************************
// Global Variables   | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
//                    V V V V V V V V V V V V V V V V V V V V V V V V V V V V V
//*****************************************************************************
SmartThingsCallout_t messageCallout;    // call out function forward decalaration
SmartThings smartthing(PIN_THING_RX, PIN_THING_TX, messageCallout);  // constructor

bool isDebugEnabled;    // enable or disable debug in this example
int door_one_state;
int door_two_state;
int door_three_state;
unsigned long currTime;

//*****************************************************************************
// Local Functions  | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
//                  V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V
//*****************************************************************************
void door_one(int cmd)
{
  int changed = 0;
  if (cmd == 1 && door_one_state == 0) {
     door_one_state = 1;
     changed = 1;
  }
  else if (cmd == 0 && door_one_state == 1) {
     door_one_state = 0;
     changed = 1;
  }

  if (changed == 1) {
     smartthing.shieldSetLED(0, 3, 1);
     digitalWrite(PIN_DOOR_ONE, HIGH);  // press button
     delay(780);
     digitalWrite(PIN_DOOR_ONE, LOW);   // unpress button
     smartthing.shieldSetLED(0, 0, 0);

     delay(14000); //delay sending final state until door should be in final state
     if (cmd == 1) {
       smartthing.send("door_one_open");
     }
     else
     {
       smartthing.send("door_one_close");
     }
     delay(500);  // don't want to send new states without some delay between
     validateDoorStates(false);
   }
}

void door_two(int cmd)
{
  int changed = 0;
  if (cmd == 1 && door_two_state == 0) {
     door_two_state = 1;
     changed = 1;
  }
  else if (cmd == 0 && door_two_state == 1) {
     door_two_state = 0;
     changed = 1;
  }

  if (changed == 1) {
     smartthing.shieldSetLED(3, 0, 2);
     digitalWrite(PIN_DOOR_ONE, HIGH);  // press button
     delay(780);
     digitalWrite(PIN_DOOR_ONE, LOW);   // unpress button
     smartthing.shieldSetLED(0, 0, 0);
     
     delay(14000); //delay sending final state until door should be in final state
     if (cmd == 1) {
       smartthing.send("door_two_open");
     }
     else
     {
       smartthing.send("door_two_close");
     }
     delay(500); // don't want to send new states without some delay between
     validateDoorStates(false);
  }
}

void door_three(int cmd)
{
  int changed = 0;
  if (cmd == 1 && door_three_state == 0) {
     door_three_state = 1;
     changed = 1;
  }
  else if (cmd == 0 && door_three_state == 1) {
     door_three_state = 0;
     changed = 1;
  }

  if (changed == 1) {
     smartthing.shieldSetLED(5, 1, 5);
     digitalWrite(PIN_DOOR_ONE, HIGH);  // press button
     delay(780);
     digitalWrite(PIN_DOOR_ONE, LOW);   // unpress button
     smartthing.shieldSetLED(0, 0, 0);

     delay(14000); //delay sending final state until door should be in final state
     if (cmd == 1) {
       smartthing.send("door_three_open");
     }
     else
     {
       smartthing.send("door_three_close");
     }
     delay(500); // don't want to send new states without some delay between
     validateDoorStates(false);
  }
}

//*****************************************************************************
// API Functions    | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
//                  V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V
//*****************************************************************************
void setup()
{
  // setup default state of global variables
  isDebugEnabled = true;
  
  // setup hardware pins 
   smartthing.shieldSetLED(0, 0, 0);
   for (int i=PIN_DOOR_ONE; i <= PIN_DOOR_THREE; i++){
      pinMode(i, OUTPUT);
      digitalWrite(i, LOW);
   }
   pinMode(PIN_DOOR_ONE_REED, INPUT_PULLUP);
   pinMode(PIN_DOOR_TWO_REED, INPUT_PULLUP);
   pinMode(PIN_DOOR_THREE_REED, INPUT_PULLUP);

   // Reed out the true states of the doors and init the cloud states for thingShield
   validateDoorStates(true);

   currTime = millis();
   
  if (isDebugEnabled)
  { // setup debug serial port
    Serial.begin(9600);         // setup serial with a baud rate of 9600
    Serial.println("setup..");  // print out 'setup..' on start
  }
}

//*****************************************************************************
void loop()
{
  // run smartthing logic 
  smartthing.run();

  // for every Nth time through this loop, validate the door states are valid per the physical switches
  if (millis() >= currTime + 2000) {
    validateDoorStates(false);
    smartthing.shieldSetLED(5,0,0);
    delay(500);
    smartthing.shieldSetLED(0,0,0);
    currTime = millis();
  }
}

//*****************************************************************************
void messageCallout(String message)
{
  // if debug is enabled print out the received message
  if (isDebugEnabled)
  {
    Serial.print("Received message: '");
    Serial.print(message);
    Serial.println("' ");
  }

  if (message.equals("door_one_open"))
  {
    door_one(1);
  }
  else if (message.equals("door_one_close"))
  {
    door_one(0);
  }
  else if (message.equals("door_two_open"))
  {
    door_two(1);
  }
  else if (message.equals("door_two_close"))
  {
    door_two(0);
  }
  else if (message.equals("door_three_open"))
  {
    door_three(1);
  }
  else if (message.equals("door_three_close"))
  {
    door_three(0);
  }
}

void validateDoorStates (bool forceSend) {

  int tmp_door_one = door_one_state;
  int tmp_door_two = door_two_state;
  int tmp_door_three = door_three_state;

  door_one_state = digitalRead(PIN_DOOR_ONE_REED) == HIGH? 1:0;
  door_two_state = digitalRead(PIN_DOOR_TWO_REED) == HIGH? 1:0;
  door_three_state = digitalRead(PIN_DOOR_THREE_REED) == HIGH? 1:0;

  if ((door_one_state != tmp_door_one) || (forceSend == true)) {
    if (door_one_state == 1) {
      smartthing.send("door_one_open");
    } else {
      smartthing.send("door_one_close");
    }
    delay(500); // don't want to send new states without some delay between
  }
  if ((door_two_state != tmp_door_two) || (forceSend == true)) {
    if (door_two_state == 1) {
      smartthing.send("door_two_open");
    } else {
      smartthing.send("door_two_close");
    }
    delay(500); // don't want to send new states without some delay between
  }
  if ((door_three_state != tmp_door_three) || (forceSend == true)) {
    if (door_three_state == 1) {
      smartthing.send("door_three_open");
    } else {
      smartthing.send("door_three_close");
    }
    delay(200); // don't want to send new states without some delay between
  }
}

SMARTTHINGS DEVICE HANDLER (ThingShield)

/**
 *  Copyright 2016 Justin Eltoft
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 *
 */
metadata {
	definition (name: "Garage Door Shield", namespace: "jdeltoft", author: "Justin Eltoft") {
		capability "Actuator"
		//capability "Switch"
		capability "Sensor"
		capability "Garage Door Control"
		capability "Door Control"
        
        //attribute "door_one", "enum", ["door_one_open", "door_one_close"]
        //attribute "door_two", "enum", ["door_two_open", "door_two_close"]
        //attribute "door_three", "enum", ["door_three_open", "door_three_close"]
        attribute "door_one", "string"
        attribute "door_two", "string"
        attribute "door_three", "string"
        
        command "door_one_open"
        command "door_one_close"
        command "door_two_open"
        command "door_two_close"
        command "door_three_open"
        command "door_three_close"
	}

	// Simulator metadata
	simulator {
		status "on":  "catchall: 0104 0000 01 01 0040 00 0A21 00 00 0000 0A 00 0A6F6E"
		status "off": "catchall: 0104 0000 01 01 0040 00 0A21 00 00 0000 0A 00 0A6F6666"

		// reply messages
		reply "raw 0x0 { 00 00 0a 0a 6f 6e }": "catchall: 0104 0000 01 01 0040 00 0A21 00 00 0000 0A 00 0A6F6E"
		reply "raw 0x0 { 00 00 0a 0a 6f 66 66 }": "catchall: 0104 0000 01 01 0040 00 0A21 00 00 0000 0A 00 0A6F6666"
	}

	// UI tile definitions
    // Is there a way to prevent any tile press if one of the three tiles here is in the transition "next" state?  I know the given tile in that transition state is locked but the others aren't
	tiles(scale: 2) {
		standardTile("door_two", "device.door_two", width: 6, height: 3, canChangeIcon: true, canChangeBackground: true, decoration: "flat") {
			state "door_two_open", label: 'Main', action: "door_two_close", icon: "st.doors.garage.garage-open", backgroundColor: "#79b821", nextState:"closing"
			state "door_two_close", label: 'Main', action: "door_two_open", icon: "st.doors.garage.garage-closed", backgroundColor: "#ffffff", nextState:"opening"
            state("opening", label:'Opening', icon:"st.doors.garage.garage-opening", backgroundColor:"#ffe71e")
			state("closing", label:'Closing', icon:"st.doors.garage.garage-closing", backgroundColor:"#ffe71e")
		}
		standardTile("door_one", "device.door_one", width: 3, height: 3, canChangeIcon: true, canChangeBackground: true, decoration: "flat") {
			state "door_one_open", label: 'Left', action: "door_one_close", icon: "st.doors.garage.garage-open", backgroundColor: "#79b821", nextState:"closing"
			state "door_one_close", label: 'Left', action: "door_one_open", icon: "st.doors.garage.garage-closed", backgroundColor: "#ffffff", nextState:"opening"
            state("opening", label:'Opening', icon:"st.doors.garage.garage-opening", backgroundColor:"#ffe71e")
			state("closing", label:'Closing', icon:"st.doors.garage.garage-closing", backgroundColor:"#ffe71e")
		}
		standardTile("door_three", "device.door_three", width: 3, height: 3, canChangeIcon: true, canChangeBackground: true, decoration: "flat") {
			state "door_three_open", label: 'Right', action: "door_three_close", icon: "st.doors.garage.garage-open", backgroundColor: "#79b821", nextState:"closing"
			state "door_three_close", label: 'Right', action: "door_three_open", icon: "st.doors.garage.garage-closed", backgroundColor: "#ffffff", nextState:"opening"
            state("opening", label:'Opening', icon:"st.doors.garage.garage-opening", backgroundColor:"#ffe71e")
			state("closing", label:'Closing', icon:"st.doors.garage.garage-closing", backgroundColor:"#ffe71e")

		}
        
		main ('door_two')
		details (['door_two', 'door_one', 'door_three'])
	}
}

def installed() {
	log.trace "installed function was called"
    //initialize()
}
def updated() {
	log.trace "updated function was called"
    //initialize()
}

def initialize() {
	log.trace "initialize function was called"
}

def refresh() {
	log.trace "refresh function was called"
}

// Parse incoming device messages to generate events
def parse(String description) {
    //log.debug "Parsing '${description}'"
    
	def value = zigbee.parse(description)?.text
	def name
	//log.trace "testing $value"
	if (value in ["door_one_open","door_one_close"]) {
       name = "door_one"
    } else if (value in ["door_two_open","door_two_close"]) {
       name = "door_two"
    } else if (value in ["door_three_open","door_three_close"]) {
       name = "door_three"
    } else {
       name = null
    }
	def result = createEvent(name: name, value: value)
	log.debug "Parse returned ${result?.descriptionText}"
	return result
}

def door_one_open() {
	zigbee.smartShield(text: "door_one_open").format()
}
def door_one_close() {
	zigbee.smartShield(text: "door_one_close").format()
}

def door_two_open() {
	zigbee.smartShield(text: "door_two_open").format()
}
def door_two_close() {
	zigbee.smartShield(text: "door_two_close").format()
}

def door_three_open() {
	zigbee.smartShield(text: "door_three_open").format()
}
def door_three_close() {
	zigbee.smartShield(text: "door_three_close").format()
}

Had a typo. All were pressing button ONE.