Integration with AT&T digital life

In that example, yourDeviceDNI is the name of the child device you want to find. You want to put the snippet with the child devices inside the webNotifyCallback() from the earlier code. Before that code In the callback, you want to set yourDeviceDNI to the name of you incoming device:

def yourDeviceDNI = params?.device

Where the params?.device is part of your callback and your URL looks something like:
https://graph…/app.id/NotifyATT?device=frontdoor&type=openclose&state=open

With the URL similar to this, you should get all of the data you need (device, type, and state) from the email over to the SmartApp and they will be in the params variable in the callback. You should be able to see them in the log.trace of the params to make sure they are coming through correctly.

I think I am beginning to understand what you are showing me now. This actually appears pretty simple to do. Once I parse out the data from the email on my raspberri pi I can pass the necessary data to the url and as long as my smartApp know what to expect, It should work. I’ve been reading the docs and I think I may start working on this tomorrow.

I still don’t get the whole child device thing. How is a child device different than a regular device? I understand parent/child relationships, I just don’t understand how a child device is different. If I am making a child device, what is it’s parent?

Sounds like you have the right idea now.

A child device is basically the same as a normal device (from what I can tell) except that it was created directly by a SmartApp. The parent is the SmartApp.

The benefits of this relationship that I can see are:

  • The SmartApp creates and configures the device rather than the user having to manually create it. Since it’s not a physical device, the normal ST device flow doesn’t work as the hub can’t find the device. Creating a device through the IDE is not user friendly or dynamic.
  • The SmartApp can say getChildDevices() to get a list of all the devices associated with it (good for updating a group of devices or deleting the devices when the user uninstalls the SmartApp)
  • The parent can easily call functions on the child by doing d = getChildDevice(deviceName) and then calling d.functionName()
  • The child device can actually call functions in the parent parent.functionname() or access parent state (parent.state.variablename) which allows them to closely communicate.

Basically, the SmartApp will handle all of the complexity of the communication with the “server” and changing state. The devices will just be “dumb” and hold the state and generate events when they are told to change state.

@swanny - does “message text to match” mean the information I am sending to smartThings like device and state?

Yes, pretty much. :smile: The params variable will contain all of your options that you send in your URL. If you send [fullurl]/Notify?deviceName=sensor1&status=open then:

params.deviceName will contain “sensor1”
params.status will contain “open”

In the snippet you asked about, I was looking for some specific text from [fullurl]/Notify?msg=messagetexthere

This new tutorial should give you a more complete example than the snippets I’ve been posting:

In this line of code, is STDeviceType and yourDeviceDNI supposed to be a hard coded value or a variable? If these are variables I don’t understand where I would get STDeviceType from. Is this something I am passing in the call back as well?

STDeviceType would be passed from your URL (params.deviceType or something like that) or hardcoded if you only have one device type that you want to use. The device DNI is the unique identifier on your hub for that specific device. In this case, you could probably just use your device name coming from the URL.

@swanny I am clearly not getting something I have been pounding at this for days now trying to figure out what I am doing wrong, but I end up getting this error every time:

java.lang.NullPointerException: Cannot get property ‘deviceChildren’ on null object @ line 75

I am starting to feel like you are writing this thing for me. :frowning: Sorry about that.
I am clearly not understanding how the function calls work and or how the data is getting populated. I have read all the documents and can’t find anything that tells me what function calls the app supports or what parameters each is expecting. Is there something I’ve missed as far as documentation goes?

If you want to take a look at the code, here it is. It’s pretty much the same thing you gave me.

This is what I have:

mappings {
      path("/Notify") {
        action: [
          GET: "webNotifyCallback"
        ]
      }
    }

    def yourDeviceDNI = params?.device

    def webNotifyCallback() {
        log.trace "get: " + params
        if (params?.msg?.contains("ContactSensor")) {
          yourDeviceDNI = params?.msg?.DeviceDNI          
        }
    }

    def setupWebNotify() {
        if (!state.accessToken) {
        	createAccessToken() 
        }
        // url to call from your web service
        log.trace "https://graph.api.smartthings.com/api/token/${state.accessToken}/smartapps/installations/${app.id}/Notify"
    }

    def d = getChildDevice(yourDeviceDNI)

    if(!d) {
        def STDeviceType = "ATT Contact Switch"  // figure out the device type from your parameters
        d = addChildDevice("keo", STDeviceType, yourDeviceDNI, null, [label:"AT&T ${yourDeviceDNI}"])
        log.trace "created ${d.displayName} with id $yourDeviceDNI"
    } else {
        log.trace "found ${d.displayName} with id $yourDeviceDNI already exists"
    }

No worries. Happy to help you try to get this going. I’ve taken your code and tried to clean it up into something you can use more directly. I have not actually run the code, but it should be pretty close.

def initialize() {
	setupWebNotify()
}

def setupWebNotify() {
    if (!state.accessToken) {
        createAccessToken() 
    }
    // url to call from your web service
    log.trace "https://graph.api.smartthings.com/api/token/${state.accessToken}/smartapps/installations/${app.id}/Notify"
}

mappings {
    path("/Notify") {
        action: [
            GET: "webNotifyCallback"
        ]
    }
}

// url would be /Notify?device=<yourdevicename>&type=ContactSensor&state=<open or closed>
def webNotifyCallback() {
    log.trace "get: " + params
    
    // get the inputs from the URL
    def deviceDNI = params?.device
    def deviceType = params?.type
    def deviceState = params?.state

	// see if the specified device exists and create it if it does not exist
    def d = getChildDevice(deviceDNI)

    if(!d) {   
    	// convert from input device type into ST device type
        def STDeviceType = null
        if (deviceType == "ContactSensor") {
        	STDeviceType = "ATT Contact Switch"
        } else {
        	log.debug "Unknown Device Type"	
        }
        
        if (STDeviceType) {
        	// we have a valid device type, create it
        	d = addChildDevice("keo", STDeviceType, deviceDNI, null, [label:"AT&T ${deviceDNI}"])
        	log.trace "created ${d.displayName} with id $yourDeviceDNI"
        }
    } else {
        log.trace "found ${d.displayName} with id $yourDeviceDNI already exists"
    }
    
    if (d) {
    	// we have a valid device that existed or was just created, now set the state
        if (deviceState == "open") {
        	// call the open function in the device type, make sure it exists and sets the device state
        	d.open()
        } else if (deviceState == "closed") {
        	// call the close function in the device type, make sure it exists and sets the device state
        	d.close() 
        } else {
        	log.debug "unknown device state = ${deviceState}"
        }
    
    }
}

@swanny - Thank you for all the help you’ve supplied. I’ll continue to work through the other components, and let you know how everything turns out.

On a somewhat different but related note: Do you have any suggestion on how I can learn to develop on smarthings better? Like I stated early on in this post, I can usually hack code pretty good, but I seem to be missing something when it comes to smartThings? I have a basic understanding of developing with java, which is what this appears to be like. Is there any documentation that can help me understand this better? The stuff I was reading isn’t really a beginners manual per say.

I think Groovy is actually a pain to learn from examples as there are a lot of ways to do the same thing. People say this makes the scripting languages like this faster to program, but that’s only for experts. For the casual person, it just make it harder to understand and easier to make mistakes IMO.

In the end, I think if you become very comfortable with java/groovy variable definitions, class definitions, and methods, the rest will come more naturally. I looked through some groovy tutorial links and most of them were just for learning groovy from java. I thought this one was a bit better and may help some:
http://www.javalobby.org/articles/groovy-intro1/
Otherwise, I would look through some learning java tutorials and just skip past the parts you are already comfortable with.

So I assume I need to create a function in the device type that sets the state attribute to open or closed. I have played around with it, but I think I am getting the syntax wrong. Am I on the right track with this?

Here is what I have in the device type:

def open() {
  def state = "open"
}

def close() {
  def state = "closed"
}

You want to something like this:

sendEvent(name: "contact", value: "open")

You also need to add the capability “capability.contactSensor” near the top of the device type.

The sendEvent goes where you have state = “open” now. :slight_smile:

That did it! Thank you so much for your help!

Now I can work on the raspberry pi part of this. You’ve been so helpful! Once I get this all working I’ll post it for other people wanting to use AT&T sensors for smartThings events. I’ve been reading that AT&T has plans to open the API to 3rd parties, but no information on when.

@swanny - Sorry its been a while since I have been able to look at this, I’ve had other priorities that have come up. So I haven’t changed any code within this app, but when I send to the app now I get an error message:
This is what the browser returns:

{“error”:true,“type”:“java.lang.IllegalArgumentException”,“message”:“An unexpected error occurred.”}

This is what the log returns:

12:17:49 PM CST: error java.lang.IllegalArgumentException: argument type mismatch @ line 100

The Code around line 100 looks like this:

  if (d) {
        //subscribeAll()
        subscribe(physicalgraph.app.DeviceWrapper)
        subscribe(physicalgraph.app.AttributeWrapper, java.util.Map)
    	// we have a valid device that existed or was just created, now set the state
        log.trace "${d.displayName} = ${deviceState}"
        if (deviceState == "open") {
        	// call the open function in the device type, make sure it exists and sets the device state
        	d.open()
        } else if (deviceState == "closed") {
            log.trace "setting device state to ${deviceState}"
        	// call the close function in the device type, make sure it exists and sets the device state
        	d.close() 
        } else {
        	log.debug "unknown device state = ${deviceState}"
        }

    }

Do you have any idea what is causing this? I though it worked before.

I’m guessing it’s one of these two lines that is causing the issue. I’m not sure what they are trying to subscribe to but with the recent upgrade to the SmartThings backend, these objects may have been changed.

Ok. What I am running into is this code will actually create the child device, but won’t set the state when I send the information via the URL. I am not a developer so I just plagiarized the code and tried to get it working. Any idea why the state won’t update even if I comment out the section that are throwing the error?

Here is the code:

/**
 *  AT&T Digital Life Hub
 *
 *  Copyright 2014 Joe Rosiak
 *
 *  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.
 *
 */
definition(
    name: "AT&T Digital Life Hub",
    namespace: "keo",
    author: "Joe Rosiak",
    description: "Interfaces AT&T Digital Life with SmartThings by using the email notifications from ATTDL.  This requires a way to run a email rule on the message body.  The rule then triggers an html call to SmartThings.  This app will also create a child device in SmartThings or Update the state of the child device if one exists.",
    category: "My Apps",
    iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
    iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png")


preferences {
	section("AT&T Sensors") {
		// TODO: put inputs here

	}
}

def installed() {
	log.debug "Installed with settings: ${settings}"

	initialize()
}

def updated() {
	log.debug "Updated with settings: ${settings}"

	unsubscribe()
	initialize()
}

def initialize() {
	setupWebNotify()
}

def setupWebNotify() {
    if (!state.accessToken) {
        createAccessToken() 
    }
    // url to call from your web service
    log.trace "https://graph.api.smartthings.com/api/token/${state.accessToken}/smartapps/installations/${app.id}/Notify"
}

mappings {
    path("/Notify") {
        action: [
            GET: "webNotifyCallback"
        ]
    }
}

// url would be /Notify?device=<yourdevicename>&type=ContactSensor&state=<open or closed>
def webNotifyCallback() {
    log.trace "get: " + params

    // get the inputs from the URL
    def deviceDNI = params?.device
    def deviceType = params?.type
    def deviceState = params?.state
	log.debug "${deviceDNI} : ${deviceType} : ${deviceState}"
	// see if the specified device exists and create it if it does not exist
    def d = getChildDevice(deviceDNI)
	log.debug "d = ${d}"
    if(!d) {   
    	// convert from input device type into ST device type
        def STDeviceType = null
        if (deviceType == "ContactSensor") {
        	STDeviceType = "ATT Contact Switch"
            //STDeviceType = "Open/Closed Sensor"
        } else {
        	log.debug "Unknown Device Type"	
        }

        if (STDeviceType) {
        	// we have a valid device type, create it
        	d = addChildDevice("keo", STDeviceType, deviceDNI, null, [label:"AT&T ${deviceDNI}"])
            //d = addChildDevice("smartthings", STDeviceType, deviceDNI, null, [label:"AT&T ${deviceDNI}"])
        	log.trace "created ${d.displayName} with id $yourDeviceDNI"
        }
    } else {
        log.trace "found ${d.displayName} with id $yourDeviceDNI already exists"
    }

    if (d) {
        //subscribeAll()
        subscribe(physicalgraph.app.DeviceWrapper)
        subscribe(physicalgraph.app.AttributeWrapper, java.util.Map)
    	// we have a valid device that existed or was just created, now set the state
        log.trace "${d.displayName} = ${deviceState}"
        if (deviceState == "open") {
        	// call the open function in the device type, make sure it exists and sets the device state
        	d.open()
        } else if (deviceState == "closed") {
            log.trace "setting device state to ${deviceState}"
        	// call the close function in the device type, make sure it exists and sets the device state
        	d.close() 
        } else {
        	log.debug "unknown device state = ${deviceState}"
        }

    }
}

Do you see your traces from webNotifyCallback() when you trigger the url? or does nothing happen at all? If nothing happens, check in your OAuth settings in the SmartApp. If you do see the traces but nothing is getting set in the device, what are you using for a DeviceType?

@swanny - it creates the child device, but fails when it tries to subscribe. If I remove the subscribe lines, the errors no longer occur but the device state never updates. After creating the child device, no other output is logged by the webNotifyCallback().

I created a new contact sensor device type. Here are my send events

def open() {
  sendEvent(name: "ATT Contact Switch", value: "open")
}
 
def close() {
  sendEvent(name: "ATT Contact Switch", value: "close")
}